From bd6a4c4b4c4609a2fe8c2c2d1d268a7b0b99ecec Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Tue, 12 May 2015 00:02:04 +0200 Subject: [PATCH 001/119] [#49] Added missing request body conversion of Wiremock to DSL --- .../wiremock/WiremockToDslConverter.groovy | 27 +++--- .../WiremockToDslConverterSpec.groovy | 84 ++++++++++++++++++- .../io/codearte/accurest/dsl/GroovyDsl.groovy | 2 +- .../accurest/dsl/internal/Request.groovy | 6 +- .../accurest/dsl/internal/Response.groovy | 23 ++--- 5 files changed, 105 insertions(+), 37 deletions(-) diff --git a/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/WiremockToDslConverter.groovy b/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/WiremockToDslConverter.groovy index bfc58cc17d..beb15bfd2b 100644 --- a/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/WiremockToDslConverter.groovy +++ b/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/WiremockToDslConverter.groovy @@ -16,6 +16,7 @@ class WiremockToDslConverter { Object wiremockStub = new JsonSlurper().parseText(wiremockStringStub) def request = wiremockStub.request def response = wiremockStub.response + def bodyPatterns = request.bodyPatterns return """\ request { ${request.method ? "method \"\"\"$request.method\"\"\"" : ""} @@ -23,18 +24,20 @@ class WiremockToDslConverter { ${request.urlPattern ? "url \$(client(regex('${escapeJava(request.urlPattern)}')), server(''))" : ""} ${request.urlPath ? "url \"\"\"$request.urlPath\"\"\"" : ""} ${ - request.headers ? """headers { - ${ - request.headers.collect { - def assertion = it.value - String headerName = it.key as String - def entry = assertion.entrySet().first() - """header(\"\"\"$headerName\"\"\", ${buildHeader(entry.key, entry.value)})\n""" - }.join('') - } - } - """ : "" - } + request.headers ? """headers { + ${ + request.headers.collect { + def assertion = it.value + String headerName = it.key as String + def entry = assertion.entrySet().first() + """header(\"\"\"$headerName\"\"\", ${buildHeader(entry.key, entry.value)})\n""" + }.join('') + } + } + """ : "" + } + ${bodyPatterns?.equalTo ? "body('''${bodyPatterns.equalTo}''')" : '' } + ${bodyPatterns?.matches ? "body \$(client(regex('${escapeJava(bodyPatterns.matches)}')), server(''))" : ""} } response { ${response.status ? "status $response.status" : ""} diff --git a/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/WiremockToDslConverterSpec.groovy b/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/WiremockToDslConverterSpec.groovy index 7466b2e7f5..a88c05681e 100755 --- a/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/WiremockToDslConverterSpec.groovy +++ b/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/WiremockToDslConverterSpec.groovy @@ -71,7 +71,7 @@ class WiremockToDslConverterSpec extends Specification { } - def 'should convert Wiremock stub with body containing simple JSON'() { + def 'should convert Wiremock stub with response body containing simple JSON'() { given: String wiremockStub = '''\ { @@ -122,7 +122,7 @@ class WiremockToDslConverterSpec extends Specification { }""") == expectedGroovyDsl } - def 'should convert Wiremock stub with body containing integer'() { + def 'should convert Wiremock stub with response body containing integer'() { given: String wiremockStub = '''\ { @@ -171,7 +171,7 @@ class WiremockToDslConverterSpec extends Specification { }""") == expectedGroovyDsl } - def 'should convert Wiremock stub with body as a list'() { + def 'should convert Wiremock stub with response body as a list'() { given: String wiremockStub = '''\ { @@ -224,7 +224,7 @@ class WiremockToDslConverterSpec extends Specification { } - def 'should convert Wiremock stub with body containing a nested list'() { + def 'should convert Wiremock stub with response body containing a nested list'() { given: String wiremockStub = '''\ { @@ -287,4 +287,80 @@ class WiremockToDslConverterSpec extends Specification { }""") == expectedGroovyDsl } + def 'should convert Wiremock stub with request body checking equality to Json'() { + given: + String wiremockStub = '''\ +{ + "request": { + "method": "POST", + "url": "/test", + "bodyPatterns": { + "equalTo": "{\\"property1\\":\\"abc\\",\\"property2\\":\\"2017-01\\",\\"property3\\":\\"666\\",\\"property4\\":1428566412}" + } + }, + "response": { + "status": 200 + } +} +''' + and: + GroovyDsl expectedGroovyDsl = GroovyDsl.make { + request { + method 'POST' + url '/test' + body ('''{"property1":"abc","property2":"2017-01","property3":"666","property4":1428566412}''') + } + response { + status 200 + } + } + when: + String groovyDsl = WiremockToDslConverter.fromWiremockStub(wiremockStub) + then: + GroovyDsl evaluatedGroovyDsl = new GroovyShell(this.class.classLoader).evaluate( + """ io.codearte.accurest.dsl.GroovyDsl.make { + $groovyDsl + }""") + and: + evaluatedGroovyDsl == expectedGroovyDsl + } + + def 'should convert Wiremock stub with request body checking matching to Json'() { + given: + String wiremockStub = '''\ +{ + "request": { + "method": "POST", + "url": "/test", + "bodyPatterns": { + "matches": "[0-9]{5}" + } + }, + "response": { + "status": 200 + } +} +''' + and: + GroovyDsl expectedGroovyDsl = GroovyDsl.make { + request { + method 'POST' + url '/test' + body $(client(~/[0-9]{5}/), server('')) + } + response { + status 200 + } + } + when: + String groovyDsl = WiremockToDslConverter.fromWiremockStub(wiremockStub) + then: + GroovyDsl evaluatedGroovyDsl = new GroovyShell(this.class.classLoader).evaluate( + """ io.codearte.accurest.dsl.GroovyDsl.make { + $groovyDsl + }""") + and: + evaluatedGroovyDsl == expectedGroovyDsl + } + } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/GroovyDsl.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/GroovyDsl.groovy index 717047341d..1007e14cec 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/GroovyDsl.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/GroovyDsl.groovy @@ -7,7 +7,7 @@ import io.codearte.accurest.dsl.internal.Request import io.codearte.accurest.dsl.internal.Response @TypeChecked -@EqualsAndHashCode(includeFields = true) +@EqualsAndHashCode @ToString(includeFields = true, includePackage = false, includeNames = true) class GroovyDsl { diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Request.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Request.groovy index 77652a9396..50bd42910c 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Request.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Request.groovy @@ -5,7 +5,7 @@ import groovy.transform.ToString import groovy.transform.TypeChecked @TypeChecked -@EqualsAndHashCode(includeFields = true) +@EqualsAndHashCode @ToString(includePackage = false, includeNames = true) class Request extends Common { @@ -64,7 +64,7 @@ class Request extends Common { } @CompileStatic -@EqualsAndHashCode(includeFields = true) +@EqualsAndHashCode @ToString(includePackage = false) class ServerRequest extends Request { ServerRequest(Request request) { @@ -73,7 +73,7 @@ class ServerRequest extends Request { } @CompileStatic -@EqualsAndHashCode(includeFields = true) +@EqualsAndHashCode @ToString(includePackage = false) class ClientRequest extends Request { ClientRequest(Request request) { diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Response.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Response.groovy index ce8a7b3b7d..3715f0c243 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Response.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Response.groovy @@ -6,13 +6,13 @@ import groovy.transform.ToString import groovy.transform.TypeChecked @TypeChecked -@EqualsAndHashCode(includeFields = true) +@EqualsAndHashCode @ToString(includePackage = false, includeFields = true) class Response extends Common { - private DslProperty status - private Headers headers - private Body body + DslProperty status + Headers headers + Body body Response() { } @@ -49,21 +49,10 @@ class Response extends Common { this.body = new Body(bodyAsValue) } - Body getBody() { - return body - } - - DslProperty getStatus() { - return status - } - - Headers getHeaders() { - return headers - } } @CompileStatic -@EqualsAndHashCode(includeFields = true) +@EqualsAndHashCode @ToString(includePackage = false) class ServerResponse extends Response { ServerResponse(Response request) { @@ -72,7 +61,7 @@ class ServerResponse extends Response { } @CompileStatic -@EqualsAndHashCode(includeFields = true) +@EqualsAndHashCode @ToString(includePackage = false) class ClientResponse extends Response { ClientResponse(Response request) { From 40bce485df1e9608fc6d98343cf222e101ab3b85 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Tue, 12 May 2015 11:15:06 +0200 Subject: [PATCH 002/119] [#49] Fixed two more issues with the conversion --- .../wiremock/WiremockToDslConverter.groovy | 39 +- .../WiremockToDslConverterSpec.groovy | 376 ++++++++++++------ build.gradle | 1 + 3 files changed, 279 insertions(+), 137 deletions(-) diff --git a/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/WiremockToDslConverter.groovy b/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/WiremockToDslConverter.groovy index beb15bfd2b..76570ee2b2 100644 --- a/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/WiremockToDslConverter.groovy +++ b/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/WiremockToDslConverter.groovy @@ -18,12 +18,12 @@ class WiremockToDslConverter { def response = wiremockStub.response def bodyPatterns = request.bodyPatterns return """\ - request { - ${request.method ? "method \"\"\"$request.method\"\"\"" : ""} - ${request.url ? "url \"\"\"$request.url\"\"\"" : ""} - ${request.urlPattern ? "url \$(client(regex('${escapeJava(request.urlPattern)}')), server(''))" : ""} - ${request.urlPath ? "url \"\"\"$request.urlPath\"\"\"" : ""} - ${ + request { + ${request.method ? "method \"\"\"$request.method\"\"\"" : ""} + ${request.url ? "url \"\"\"$request.url\"\"\"" : ""} + ${request.urlPattern ? "url \$(client(regex('${escapeJava(request.urlPattern)}')), server(''))" : ""} + ${request.urlPath ? "url \"\"\"$request.urlPath\"\"\"" : ""} + ${ request.headers ? """headers { ${ request.headers.collect { @@ -36,20 +36,21 @@ class WiremockToDslConverter { } """ : "" } - ${bodyPatterns?.equalTo ? "body('''${bodyPatterns.equalTo}''')" : '' } - ${bodyPatterns?.matches ? "body \$(client(regex('${escapeJava(bodyPatterns.matches)}')), server(''))" : ""} - } - response { - ${response.status ? "status $response.status" : ""} - ${response.body ? "body( ${buildBody(response.body)})" : ""} - ${ + ${bodyPatterns?.equalTo?.every { it } ? "body('''${bodyPatterns.equalTo[0]}''')" : ''} + ${bodyPatterns?.equalToJson?.every { it } ? "body('''${bodyPatterns.equalToJson[0]}''')" : ''} + ${bodyPatterns?.matches?.every { it } ? "body \$(client(regex('${escapeJava(bodyPatterns.matches[0])}')), server(''))" : ""} + } + response { + ${response.status ? "status $response.status" : ""} + ${response.body ? "body( ${buildBody(response.body)})" : ""} + ${ response.headers ? """headers { - ${response.headers.collect { "header('$it.key': '${it.value}')\n" }.join('')} - } - """ : "" + ${response.headers.collect { "header('$it.key': '${it.value}')\n" }.join('')} + } + """ : "" } - } - """ + } + """ } private String buildHeader(String method, Object value) { @@ -158,7 +159,7 @@ class WiremockToDslConverter { static String wrapWithFactoryMethod(String dslFromWiremockStub) { return """\ ${GroovyDsl.name}.make { - $dslFromWiremockStub + $dslFromWiremockStub } """ } diff --git a/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/WiremockToDslConverterSpec.groovy b/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/WiremockToDslConverterSpec.groovy index a88c05681e..98d74e6ca2 100755 --- a/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/WiremockToDslConverterSpec.groovy +++ b/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/WiremockToDslConverterSpec.groovy @@ -1,5 +1,6 @@ package io.codearte.accurest.wiremock +import com.github.tomakehurst.wiremock.stubbing.StubMapping import io.codearte.accurest.dsl.GroovyDsl import spock.lang.Specification @@ -9,27 +10,29 @@ class WiremockToDslConverterSpec extends Specification { given: String wiremockStub = '''\ { - "request": { - "method": "GET", - "url": "/path", - "headers" : { - "Accept": { - "matches": "text/.*" - }, - "X-Custom-Header": { - "contains": "2134" - } - } - }, - "response": { - "status": 200, - "body": "{ \\"id\\": { \\"value\\": \\"132\\" }, \\"surname\\": \\"Kowalsky\\", \\"name\\": \\"Jan\\", \\"created\\": \\"2014-02-02 12:23:43\\" }", - "headers": { - "Content-Type": "text/plain", - } - } + "request": { + "method": "GET", + "url": "/path", + "headers" : { + "Accept": { + "matches": "text/.*" + }, + "X-Custom-Header": { + "contains": "2134" + } + } + }, + "response": { + "status": 200, + "body": "{ \\"id\\": { \\"value\\": \\"132\\" }, \\"surname\\": \\"Kowalsky\\", \\"name\\": \\"Jan\\", \\"created\\": \\"2014-02-02 12:23:43\\" }", + "headers": { + "Content-Type": "text/plain" + } + } } ''' + and: + stubMappingIsValidWiremockStub(wiremockStub) and: GroovyDsl expectedGroovyDsl = GroovyDsl.make { request { @@ -66,8 +69,8 @@ class WiremockToDslConverterSpec extends Specification { then: new GroovyShell(this.class.classLoader).evaluate( """ io.codearte.accurest.dsl.GroovyDsl.make { - $groovyDsl - }""") == expectedGroovyDsl + $groovyDsl + }""") == expectedGroovyDsl } @@ -75,24 +78,26 @@ class WiremockToDslConverterSpec extends Specification { given: String wiremockStub = '''\ { - "request": { - "method": "DELETE", - "urlPattern": "/credit-card-verification-data/[0-9]+", - "headers": { - "Content-Type": { - "equalTo": "application/vnd.mymoid-adapter.v2+json; charset=UTF-8" - } - } - }, - "response": { - "status": 200, - "body": "{\\"status\\": \\"OK\\"}", - "headers": { - "Content-Type": "application/json" - } - } + "request": { + "method": "DELETE", + "urlPattern": "/credit-card-verification-data/[0-9]+", + "headers": { + "Content-Type": { + "equalTo": "application/vnd.mymoid-adapter.v2+json; charset=UTF-8" + } + } + }, + "response": { + "status": 200, + "body": "{\\"status\\": \\"OK\\"}", + "headers": { + "Content-Type": "application/json" + } + } } ''' + and: + stubMappingIsValidWiremockStub(wiremockStub) and: GroovyDsl expectedGroovyDsl = GroovyDsl.make { request { @@ -105,7 +110,7 @@ class WiremockToDslConverterSpec extends Specification { response { status 200 body("""{ - "status": "OK" + "status": "OK" }""") headers { header 'Content-Type': 'application/json' @@ -118,8 +123,8 @@ class WiremockToDslConverterSpec extends Specification { then: new GroovyShell(this.class.classLoader).evaluate( """ io.codearte.accurest.dsl.GroovyDsl.make { - $groovyDsl - }""") == expectedGroovyDsl + $groovyDsl + }""") == expectedGroovyDsl } def 'should convert Wiremock stub with response body containing integer'() { @@ -127,23 +132,25 @@ class WiremockToDslConverterSpec extends Specification { String wiremockStub = '''\ { "request": { - "method": "POST", - "url": "/charge/count", - "headers": { - "Content-Type": { - "equalTo": "application/vnd.creditcard-reporter.v1+json" - } - } + "method": "POST", + "url": "/charge/count", + "headers": { + "Content-Type": { + "equalTo": "application/vnd.creditcard-reporter.v1+json" + } + } }, "response": { - "status": 200, - "body": 200, - "headers": { - "Content-Type": "application/json" - } + "status": 200, + "body": 200, + "headers": { + "Content-Type": "application/json" + } } } ''' + and: + stubMappingIsValidWiremockStub(wiremockStub) and: GroovyDsl expectedGroovyDsl = GroovyDsl.make { request { @@ -167,8 +174,8 @@ class WiremockToDslConverterSpec extends Specification { then: new GroovyShell(this.class.classLoader).evaluate( """ io.codearte.accurest.dsl.GroovyDsl.make { - $groovyDsl - }""") == expectedGroovyDsl + $groovyDsl + }""") == expectedGroovyDsl } def 'should convert Wiremock stub with response body as a list'() { @@ -176,23 +183,25 @@ class WiremockToDslConverterSpec extends Specification { String wiremockStub = '''\ { "request": { - "method": "POST", - "url": "/charge/count", - "headers": { - "Content-Type": { - "equalTo": "application/vnd.creditcard-reporter.v1+json" - } - } + "method": "POST", + "url": "/charge/count", + "headers": { + "Content-Type": { + "equalTo": "application/vnd.creditcard-reporter.v1+json" + } + } }, "response": { - "status": 200, - "body": "[ {\\"a\\":1, \\"c\\":\\"3\\"}, \\"b\\", \\"a\\" ]", - "headers": { - "Content-Type": "application/json" - } + "status": 200, + "body": "[ {\\"a\\":1, \\"c\\":\\"3\\"}, \\"b\\", \\"a\\" ]", + "headers": { + "Content-Type": "application/json" + } } } ''' + and: + stubMappingIsValidWiremockStub(wiremockStub) and: GroovyDsl expectedGroovyDsl = GroovyDsl.make { request { @@ -219,8 +228,8 @@ class WiremockToDslConverterSpec extends Specification { then: new GroovyShell(this.class.classLoader).evaluate( """ io.codearte.accurest.dsl.GroovyDsl.make { - $groovyDsl - }""") == expectedGroovyDsl + $groovyDsl + }""") == expectedGroovyDsl } @@ -229,20 +238,22 @@ class WiremockToDslConverterSpec extends Specification { String wiremockStub = '''\ { "request": { - "method": "POST", - "url": "/charge/search?pageNumber=0&size=2147483647", - "headers": { - "Content-Type": { - "equalTo": "application/vnd.creditcard-reporter.v1+json" - } - } + "method": "POST", + "url": "/charge/search?pageNumber=0&size=2147483647", + "headers": { + "Content-Type": { + "equalTo": "application/vnd.creditcard-reporter.v1+json" + } + } }, "response": { - "status": 200, - "body":"[{\\"amount\\":1.01,\\"name\\":\\"Name\\",\\"info\\":{\\"title\\":\\"title1\\",\\"payload\\":null},\\"booleanvalue\\":true,\\"user\\":null},{\\"amount\\":2.01,\\"name\\":\\"Name2\\",\\"info\\":{\\"title\\":\\"title2\\",\\"payload\\":null},\\"booleanvalue\\":true,\\"user\\":null}]" - } + "status": 200, + "body":"[{\\"amount\\":1.01,\\"name\\":\\"Name\\",\\"info\\":{\\"title\\":\\"title1\\",\\"payload\\":null},\\"booleanvalue\\":true,\\"user\\":null},{\\"amount\\":2.01,\\"name\\":\\"Name2\\",\\"info\\":{\\"title\\":\\"title2\\",\\"payload\\":null},\\"booleanvalue\\":true,\\"user\\":null}]" + } } ''' + and: + stubMappingIsValidWiremockStub(wiremockStub) and: GroovyDsl expectedGroovyDsl = GroovyDsl.make { request { @@ -255,26 +266,26 @@ class WiremockToDslConverterSpec extends Specification { response { status 200 body("""[ - { - "amount": 1.01, - "name": "Name", - "info": { - "title": "title1", - "payload": null - }, - "booleanvalue": true, - "user": null - }, - { - "amount": 2.01, - "name": "Name2", - "info": { - "title": "title2", - "payload": null - }, - "booleanvalue": true, - "user": null - } + { + "amount": 1.01, + "name": "Name", + "info": { + "title": "title1", + "payload": null + }, + "booleanvalue": true, + "user": null + }, + { + "amount": 2.01, + "name": "Name2", + "info": { + "title": "title2", + "payload": null + }, + "booleanvalue": true, + "user": null + } ]""") } } @@ -283,8 +294,8 @@ class WiremockToDslConverterSpec extends Specification { then: new GroovyShell(this.class.classLoader).evaluate( """ io.codearte.accurest.dsl.GroovyDsl.make { - $groovyDsl - }""") == expectedGroovyDsl + $groovyDsl + }""") == expectedGroovyDsl } def 'should convert Wiremock stub with request body checking equality to Json'() { @@ -292,17 +303,19 @@ class WiremockToDslConverterSpec extends Specification { String wiremockStub = '''\ { "request": { - "method": "POST", - "url": "/test", - "bodyPatterns": { - "equalTo": "{\\"property1\\":\\"abc\\",\\"property2\\":\\"2017-01\\",\\"property3\\":\\"666\\",\\"property4\\":1428566412}" - } + "method": "POST", + "url": "/test", + "bodyPatterns": [{ + "equalTo": "{\\"property1\\":\\"abc\\",\\"property2\\":\\"2017-01\\",\\"property3\\":\\"666\\",\\"property4\\":1428566412}" + }] }, "response": { - "status": 200 - } + "status": 200 + } } ''' + and: + stubMappingIsValidWiremockStub(wiremockStub) and: GroovyDsl expectedGroovyDsl = GroovyDsl.make { request { @@ -319,8 +332,8 @@ class WiremockToDslConverterSpec extends Specification { then: GroovyDsl evaluatedGroovyDsl = new GroovyShell(this.class.classLoader).evaluate( """ io.codearte.accurest.dsl.GroovyDsl.make { - $groovyDsl - }""") + $groovyDsl + }""") and: evaluatedGroovyDsl == expectedGroovyDsl } @@ -330,17 +343,19 @@ class WiremockToDslConverterSpec extends Specification { String wiremockStub = '''\ { "request": { - "method": "POST", - "url": "/test", - "bodyPatterns": { - "matches": "[0-9]{5}" - } + "method": "POST", + "url": "/test", + "bodyPatterns": [{ + "matches": "[0-9]{5}" + }] }, "response": { - "status": 200 - } + "status": 200 + } } ''' + and: + stubMappingIsValidWiremockStub(wiremockStub) and: GroovyDsl expectedGroovyDsl = GroovyDsl.make { request { @@ -357,10 +372,135 @@ class WiremockToDslConverterSpec extends Specification { then: GroovyDsl evaluatedGroovyDsl = new GroovyShell(this.class.classLoader).evaluate( """ io.codearte.accurest.dsl.GroovyDsl.make { - $groovyDsl - }""") + $groovyDsl + }""") and: evaluatedGroovyDsl == expectedGroovyDsl } + def 'should convert Wiremock stub with request body with equalToJson'() { + given: + String wiremockStub = '''\ +{ + "request" : { + "url" : "/test", + "method" : "POST", + "bodyPatterns" : [ { + "equalToJson" : "{\\"pan\\":\\"4855141150107894\\",\\"expirationDate\\":\\"2017-01\\",\\"dcvx\\":\\"178\\"}", + "jsonCompareMode" : "LENIENT" + } ] + }, + "response" : { + "status" : 200 + } +} +''' + and: + stubMappingIsValidWiremockStub(wiremockStub) + and: + GroovyDsl expectedGroovyDsl = GroovyDsl.make { + request { + method 'POST' + url '/test' + body '''{"pan":"4855141150107894","expirationDate":"2017-01","dcvx":"178"}''' + } + response { + status 200 + } + } + when: + String groovyDsl = WiremockToDslConverter.fromWiremockStub(wiremockStub) + then: + GroovyDsl evaluatedGroovyDsl = new GroovyShell(this.class.classLoader).evaluate( + """ io.codearte.accurest.dsl.GroovyDsl.make { + $groovyDsl + }""") + and: + evaluatedGroovyDsl == expectedGroovyDsl + } + + def 'should convert Wiremock stub with request body with equalTo'() { + given: + String wiremockStub = '''\ + { + "request" : { + "url" : "/test", + "method" : "POST", + "bodyPatterns" : [ { + "equalTo" : "{\\"pan\\":\\"4855141150107894\\",\\"expirationDate\\":\\"2017-01\\",\\"dcvx\\":\\"178\\"}" + } ] + }, + "response" : { + "status" : 200 + } + } + ''' + and: + stubMappingIsValidWiremockStub(wiremockStub) + and: + GroovyDsl expectedGroovyDsl = GroovyDsl.make { + request { + method 'POST' + url '/test' + body '''{"pan":"4855141150107894","expirationDate":"2017-01","dcvx":"178"}''' + } + response { + status 200 + } + } + when: + String groovyDsl = WiremockToDslConverter.fromWiremockStub(wiremockStub) + then: + GroovyDsl evaluatedGroovyDsl = new GroovyShell(this.class.classLoader).evaluate( + """ io.codearte.accurest.dsl.GroovyDsl.make { + $groovyDsl + }""") + and: + evaluatedGroovyDsl == expectedGroovyDsl + } + + def 'should convert Wiremock stub with request body with matches'() { + given: + String wiremockStub = '''\ + { + "request" : { + "url" : "/test", + "method" : "POST", + "bodyPatterns" : [ { + "matches" : "[0-9]{2}" + } ] + }, + "response" : { + "status" : 200 + } + } + ''' + and: + stubMappingIsValidWiremockStub(wiremockStub) + and: + GroovyDsl expectedGroovyDsl = GroovyDsl.make { + request { + method 'POST' + url '/test' + body $(client(~/[0-9]{2}/), server('')) + } + response { + status 200 + } + } + when: + String groovyDsl = WiremockToDslConverter.fromWiremockStub(wiremockStub) + then: + GroovyDsl evaluatedGroovyDsl = new GroovyShell(this.class.classLoader).evaluate( + """ io.codearte.accurest.dsl.GroovyDsl.make { + $groovyDsl + }""") + and: + evaluatedGroovyDsl == expectedGroovyDsl + } + + void stubMappingIsValidWiremockStub(String mappingDefinition) { + StubMapping.buildFrom(mappingDefinition) + } + } diff --git a/build.gradle b/build.gradle index c2d42cfdf1..28d6b6d0a2 100644 --- a/build.gradle +++ b/build.gradle @@ -98,6 +98,7 @@ project(':accurest-converters') { compile project(':accurest-core') compile 'org.apache.commons:commons-lang3:3.3.2' compile 'commons-io:commons-io:[2.4,)' + testCompile 'com.github.tomakehurst:wiremock:1.53' } } From f581bb561f93406ff0da8c8eecf9d96a3af4155e Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Tue, 12 May 2015 12:20:25 +0200 Subject: [PATCH 003/119] [#5] Adding assertions of patterns --- .../wiremock/WiremockToDslConverter.groovy | 6 ++++-- .../wiremock/WiremockToDslConverterSpec.groovy | 6 +++--- .../codearte/accurest/dsl/internal/Common.groovy | 14 ++++++++++++++ build.gradle | 4 ++++ 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/WiremockToDslConverter.groovy b/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/WiremockToDslConverter.groovy index 76570ee2b2..543f9eff89 100644 --- a/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/WiremockToDslConverter.groovy +++ b/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/WiremockToDslConverter.groovy @@ -4,6 +4,7 @@ import groovy.json.JsonOutput import groovy.json.JsonSlurper import groovy.xml.XmlUtil import io.codearte.accurest.dsl.GroovyDsl +import nl.flotsam.xeger.Xeger import static org.apache.commons.lang3.StringEscapeUtils.escapeJava @@ -17,11 +18,12 @@ class WiremockToDslConverter { def request = wiremockStub.request def response = wiremockStub.response def bodyPatterns = request.bodyPatterns + String urlPattern = request.urlPattern return """\ request { ${request.method ? "method \"\"\"$request.method\"\"\"" : ""} ${request.url ? "url \"\"\"$request.url\"\"\"" : ""} - ${request.urlPattern ? "url \$(client(regex('${escapeJava(request.urlPattern)}')), server(''))" : ""} + ${urlPattern ? "url \$(client(regex('${escapeJava(urlPattern)}')), server('${new Xeger(escapeJava(urlPattern)).generate()}'))" : ""} ${request.urlPath ? "url \"\"\"$request.urlPath\"\"\"" : ""} ${ request.headers ? """headers { @@ -38,7 +40,7 @@ class WiremockToDslConverter { } ${bodyPatterns?.equalTo?.every { it } ? "body('''${bodyPatterns.equalTo[0]}''')" : ''} ${bodyPatterns?.equalToJson?.every { it } ? "body('''${bodyPatterns.equalToJson[0]}''')" : ''} - ${bodyPatterns?.matches?.every { it } ? "body \$(client(regex('${escapeJava(bodyPatterns.matches[0])}')), server(''))" : ""} + ${bodyPatterns?.matches?.every { it } ? "body \$(client(regex('${escapeJava(bodyPatterns.matches[0])}')), server('${new Xeger(escapeJava(bodyPatterns.matches[0])).generate()}'))" : ""} } response { ${response.status ? "status $response.status" : ""} diff --git a/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/WiremockToDslConverterSpec.groovy b/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/WiremockToDslConverterSpec.groovy index 98d74e6ca2..b147d9da93 100755 --- a/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/WiremockToDslConverterSpec.groovy +++ b/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/WiremockToDslConverterSpec.groovy @@ -102,7 +102,7 @@ class WiremockToDslConverterSpec extends Specification { GroovyDsl expectedGroovyDsl = GroovyDsl.make { request { method 'DELETE' - url $(client(~/\/credit-card-verification-data\/[0-9]+/), server('')) + url $(client(~/\/credit-card-verification-data\/[0-9]+/), server('/credit-card-verification-data/1')) headers { header('Content-Type': 'application/vnd.mymoid-adapter.v2+json; charset=UTF-8') } @@ -361,7 +361,7 @@ class WiremockToDslConverterSpec extends Specification { request { method 'POST' url '/test' - body $(client(~/[0-9]{5}/), server('')) + body $(client(~/[0-9]{5}/), server('12345')) } response { status 200 @@ -482,7 +482,7 @@ class WiremockToDslConverterSpec extends Specification { request { method 'POST' url '/test' - body $(client(~/[0-9]{2}/), server('')) + body $(client(~/[0-9]{2}/), server('12')) } response { status 200 diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Common.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Common.groovy index 91549f6409..608f15eec3 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Common.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Common.groovy @@ -47,10 +47,12 @@ class Common { } DslProperty value(ClientDslProperty client, ServerDslProperty server) { + assertThatSidesMatch(client.clientValue, server.serverValue) return new DslProperty(client.clientValue, server.serverValue) } DslProperty value(ServerDslProperty server, ClientDslProperty client) { + assertThatSidesMatch(client.clientValue, server.serverValue) return new DslProperty(client.clientValue, server.serverValue) } @@ -77,4 +79,16 @@ class Common { ServerDslProperty server(Object serverValue) { return new ServerDslProperty(serverValue) } + + void assertThatSidesMatch(Pattern firstSide, Object secondSide) { + assert secondSide ==~ firstSide + } + + void assertThatSidesMatch(Object firstSide, Pattern secondSide) { + assert secondSide ==~ firstSide + } + + void assertThatSidesMatch(Object firstSide, Object secondSide) { + // do nothing + } } diff --git a/build.gradle b/build.gradle index 28d6b6d0a2..783681eebd 100644 --- a/build.gradle +++ b/build.gradle @@ -47,6 +47,9 @@ subprojects { repositories { mavenLocal() mavenCentral() + maven { + url "https://jitpack.io" + } } //Dependencies in all subprojects - http://solidsoft.wordpress.com/2014/11/13/gradle-tricks-display-dependencies-for-all-subprojects-in-multi-project-build/ @@ -98,6 +101,7 @@ project(':accurest-converters') { compile project(':accurest-core') compile 'org.apache.commons:commons-lang3:3.3.2' compile 'commons-io:commons-io:[2.4,)' + compile 'com.github.marcingrzejszczak:xeger:20130128' testCompile 'com.github.tomakehurst:wiremock:1.53' } } From 613b2d800ddb627f19ac63f93813f50f06bb20ce Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Tue, 12 May 2015 12:43:19 +0200 Subject: [PATCH 004/119] [#5] Added missing file --- .../io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy index 4ff7fa09f5..b813ee11ef 100755 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy @@ -239,7 +239,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { GroovyDsl groovyDsl = GroovyDsl.make { request { url $( - client(~/\/^[0-9]{2}$/), + client(~/^\/[0-9]{2}$/), server('/12') ) } @@ -247,7 +247,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { expect: new WiremockRequestStubStrategy(groovyDsl).buildClientRequestContent() == new JsonSlurper().parseText(''' { - "urlPattern":"/^[0-9]{2}$" + "urlPattern":"^/[0-9]{2}$" } ''') } From 7e7e3ff003c3da920ca24765f8d653cdbc0e91b7 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Tue, 12 May 2015 16:14:46 +0200 Subject: [PATCH 005/119] [#49] Fixed missing conversion of request body --- .../dsl/WiremockRequestStubStrategy.groovy | 8 ++- .../dsl/WiremockResponseStubStrategy.groovy | 12 ++-- .../accurest/dsl/internal/Body.groovy | 34 +++++++++-- .../accurest/util/JsonConverter.groovy | 38 ++++++++++++ .../accurest/util/StubMappingConverter.groovy | 53 ----------------- .../builder/SpockMethodBuilderSpec.groovy | 59 +++++++++++++++++++ 6 files changed, 139 insertions(+), 65 deletions(-) create mode 100644 accurest-core/src/main/groovy/io/codearte/accurest/util/JsonConverter.groovy delete mode 100644 accurest-core/src/main/groovy/io/codearte/accurest/util/StubMappingConverter.groovy diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy index 36f7ec1ab5..97a7e942ba 100755 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy @@ -3,6 +3,7 @@ import groovy.transform.PackageScope import groovy.transform.TypeChecked import io.codearte.accurest.dsl.internal.ClientRequest import io.codearte.accurest.dsl.internal.Request +import io.codearte.accurest.util.JsonConverter import java.util.regex.Pattern @@ -38,9 +39,8 @@ class WiremockRequestStubStrategy extends BaseWiremockStubStrategy { return [:] } if (containsRegex(body)) { - return [bodyPatterns: [[matches: parseBody(body)]]] + return [bodyPatterns: [[matches: parseBody(JsonConverter.transformValues(body, { it.toString() }))]]] } - return [bodyPatterns: [[equalTo: parseBody(body)]]] } @@ -49,4 +49,8 @@ class WiremockRequestStubStrategy extends BaseWiremockStubStrategy { return (bodyString =~ /\^.*\$/).find() } + boolean containsRegex(Map map) { + return map.values().any { it instanceof Pattern } + } + } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockResponseStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockResponseStubStrategy.groovy index e0b7556547..b517150236 100755 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockResponseStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockResponseStubStrategy.groovy @@ -21,12 +21,12 @@ class WiremockResponseStubStrategy extends BaseWiremockStubStrategy { private Map buildResponseContent(ClientResponse response) { return ([status : response?.status?.clientValue, - headers: buildClientResponseHeadersSection(response.headers) - ] << appendBody(response)).findAll { it.value } + headers: buildClientResponseHeadersSection(response.headers) + ] << appendBody(response)).findAll { it.value } } - private Map appendBody(ClientResponse response) { - Object body = response?.body?.clientValue - return body != null ? [body: parseBody(body)] : [:] - } + private Map appendBody(ClientResponse response) { + Object body = response?.body?.clientValue + return body != null ? [body: parseBody(body)] : [:] + } } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Body.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Body.groovy index 5e8d46ed98..da602b5ebc 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Body.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Body.groovy @@ -3,12 +3,19 @@ package io.codearte.accurest.dsl.internal import groovy.json.JsonSlurper import groovy.transform.EqualsAndHashCode import groovy.transform.ToString +import io.codearte.accurest.util.JsonConverter import org.codehaus.groovy.runtime.GStringImpl +import java.util.regex.Matcher +import java.util.regex.Pattern + @ToString(includePackage = false, includeFields = true, includeNames = true) @EqualsAndHashCode(includeFields = true) class Body extends DslProperty { + private static final Pattern TEMPORARY_PATTERN_HOLDER = Pattern.compile('REGEXP>>(.*)<<') + private static final String JSON_VALUE_PATTERN_FOR_REGEX = 'REGEXP>>%s<<' + Body(Map body) { super(extractValue(body, {it.clientValue}), extractValue(body, {it.serverValue})) } @@ -36,9 +43,28 @@ class Body extends DslProperty { } private static Object extractValue(GString bodyAsValue, Closure valueProvider) { - GString clientGString = new GStringImpl(bodyAsValue.values.clone(), bodyAsValue.strings.clone()) - Object[] clientValues = bodyAsValue.values.collect { it instanceof DslProperty ? valueProvider(it) : it } as Object[] - return new JsonSlurper().parseText(new GStringImpl(clientValues, clientGString.strings).toString()) + GString gString = new GStringImpl(bodyAsValue.values.clone(), bodyAsValue.strings.clone()) + Object[] values = bodyAsValue.values.collect { it instanceof DslProperty ? valueProvider(it) : it } as Object[] + Object[] valuesWithRegexpsAsTransformedStrings = values.collect { + it instanceof Pattern ? String.format(JSON_VALUE_PATTERN_FOR_REGEX, it.toString()) : it + } as Object[] + def parsedJson = new JsonSlurper().parseText(new GStringImpl(valuesWithRegexpsAsTransformedStrings, gString.strings)) + return convertAllTemporaryRegexPlaceholdersBackToPatterns(parsedJson) } - + + private static Object convertAllTemporaryRegexPlaceholdersBackToPatterns(parsedJson) { + JsonConverter.transformValues(parsedJson, { Object value -> + if (value instanceof String) { + String string = (String) value + Matcher matcher = TEMPORARY_PATTERN_HOLDER.matcher(string) + if (matcher.matches()) { + String pattern = matcher[0][1] + return Pattern.compile(pattern) + } + return value + } + return value + }) + } + } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonConverter.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonConverter.groovy new file mode 100644 index 0000000000..208a3aefa3 --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonConverter.groovy @@ -0,0 +1,38 @@ +package io.codearte.accurest.util + +import groovy.json.JsonSlurper +/** + * @author Marcin Grzejszczak + */ +class JsonConverter { + + private static Map convert(Map map, Closure closure) { + return map.collectEntries { + key, value -> + [key, transformValues(value, closure)] + } + } + + static def transformValues(def value, Closure closure) { + if (value instanceof String && value) { + try { + def json = new JsonSlurper().parseText(value) + if (json instanceof Map) { + return convert(json, closure) + } + } catch (Exception ignore) { + return closure(value) + } + } else if (value instanceof Map) { + return convert(value as Map, closure) + } else if (value instanceof List) { + return value.collect({ transformValues(it, closure) }) + } + try { + return closure(value) + } catch (Exception ignore) { + return value + } + } + +} diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/util/StubMappingConverter.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/StubMappingConverter.groovy deleted file mode 100644 index f1ce59959f..0000000000 --- a/accurest-core/src/main/groovy/io/codearte/accurest/util/StubMappingConverter.groovy +++ /dev/null @@ -1,53 +0,0 @@ -package io.codearte.accurest.util - -import groovy.json.JsonException -import groovy.json.JsonSlurper - -import java.util.regex.Pattern - -/** - * @author Marcin Grzejszczak - */ -class StubMappingConverter { - - private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile(/^\$\{(.*):(.*)\}$/) - public static final int SERVER_SIDE_GROUP = 2 - - static Map toStubMappingOnServerSide(File stubMapping) { - def json = new JsonSlurper().parse(stubMapping) - return convertPlaceholders(json as Map, { String value -> - getGroupFromMatchingPattern(value) - }) - } - - private static Map convertPlaceholders(Map map, Closure closure) { - return map.collectEntries { - key, value -> - [key, transformValue(value, closure)] - } - } - - static def transformValue(def value, Closure closure) { - if (value instanceof String && value) { - try { - def json = new JsonSlurper().parseText(value) - if (json instanceof Map) { - return convertPlaceholders(json, closure) - } - } catch (JsonException ignore) { - return closure(value) - } - } else if (value instanceof Map) { - return convertPlaceholders(value as Map, closure) - } else if (value instanceof List) { - return value.collect({ transformValue(it, closure) }) - } - - return value - } - - private static Object getGroupFromMatchingPattern(String value) { - return value.matches(PLACEHOLDER_PATTERN) ? PLACEHOLDER_PATTERN.matcher(value)[0][SERVER_SIDE_GROUP] : value - } - -} diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy index c68a96a1e6..39f2b4762f 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy @@ -110,4 +110,63 @@ class SpockMethodBuilderSpec extends Specification { blockBuilder.toString().contains("responseBody.property1 == \"a\"") blockBuilder.toString().contains("responseBody.property2.property3 == \"b\"") } + + def "should generate regex assertions for map objects in response body"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "GET" + url "test" + } + response { + status 200 + body( + property1: "a", + property2: value( + client(''), + server(regex('\\\\d{3}')) + ) + ) + headers { + header('Content-Type': 'application/json') + + } + + } + } + SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains("responseBody.property1 == \"a\"") + blockBuilder.toString().contains("responseBody.property2 ==~ java.util.regex.Pattern.compile('\\\\d{3}')") + } + + def "should generate regex assertions for string objects in response body"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "GET" + url "test" + } + response { + status 200 + body( """{"property1":"a","property2":"${value(client('123'), server(regex('[0-9]{3}')))}"}""") + headers { + header('Content-Type': 'application/json') + + } + + } + } + SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains("responseBody.property1 == \"a\"") + blockBuilder.toString().contains("responseBody.property2 ==~ java.util.regex.Pattern.compile('[0-9]{3}')") + } + } From f84f11611b88c23f087b8fce1e7ad18adca0cae9 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Tue, 12 May 2015 16:20:42 +0200 Subject: [PATCH 006/119] [#49] Surrounded with different quotes --- .../accurest/wiremock/WiremockToDslConverter.groovy | 4 ++-- .../io/codearte/accurest/dsl/internal/Body.groovy | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/WiremockToDslConverter.groovy b/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/WiremockToDslConverter.groovy index 76570ee2b2..00d4d3786e 100644 --- a/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/WiremockToDslConverter.groovy +++ b/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/WiremockToDslConverter.groovy @@ -36,8 +36,8 @@ class WiremockToDslConverter { } """ : "" } - ${bodyPatterns?.equalTo?.every { it } ? "body('''${bodyPatterns.equalTo[0]}''')" : ''} - ${bodyPatterns?.equalToJson?.every { it } ? "body('''${bodyPatterns.equalToJson[0]}''')" : ''} + ${bodyPatterns?.equalTo?.every { it } ? "body(\"\"\"${bodyPatterns.equalTo[0]}\"\"\")" : ''} + ${bodyPatterns?.equalToJson?.every { it } ? "body(\"\"\"${bodyPatterns.equalToJson[0]}\"\"\")" : ''} ${bodyPatterns?.matches?.every { it } ? "body \$(client(regex('${escapeJava(bodyPatterns.matches[0])}')), server(''))" : ""} } response { diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Body.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Body.groovy index da602b5ebc..315135d939 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Body.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Body.groovy @@ -42,6 +42,18 @@ class Body extends DslProperty { super(bodyAsValue.clientValue, bodyAsValue.serverValue) } + /** + * Due to the fact that we allow users to have a body with GString and different values inside + * we need to be prepared that they pass regexps around both on client and server side. + * + * In order to preserve the original JSON structure we need to convert the passed Regex patterns + * to a temporary string, then convert all to a legitimate JSON structure and then finally + * convert it back from string to a pattern. + * + * @param bodyAsValue - GString with passed values + * @param valueProvider - provider of values either for server or client side + * @return JSON structure with replaced client / server side parts + */ private static Object extractValue(GString bodyAsValue, Closure valueProvider) { GString gString = new GStringImpl(bodyAsValue.values.clone(), bodyAsValue.strings.clone()) Object[] values = bodyAsValue.values.collect { it instanceof DslProperty ? valueProvider(it) : it } as Object[] From 9b9bca31614afba8cf4c97c1a9340579e5292bfc Mon Sep 17 00:00:00 2001 From: Jakub Kubrynski Date: Tue, 12 May 2015 23:14:28 +0200 Subject: [PATCH 007/119] Release version: 0.6.1 [ci skip] From f6721b0ca646ffed0beb7997d5f47390a49da6c1 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Tue, 12 May 2015 23:36:43 +0200 Subject: [PATCH 008/119] Bundled Xeger together with the application --- .../main/groovy/nl/flotsam/xeger/Xeger.java | 114 ++++++++++++++++++ .../groovy/nl/flotsam/xeger/XegerTest.java | 64 ++++++++++ .../nl/flotsam/xeger/XegerUtilsTest.java | 39 ++++++ build.gradle | 6 +- 4 files changed, 219 insertions(+), 4 deletions(-) create mode 100644 accurest-converters/src/main/groovy/nl/flotsam/xeger/Xeger.java create mode 100644 accurest-converters/src/test/groovy/nl/flotsam/xeger/XegerTest.java create mode 100644 accurest-converters/src/test/groovy/nl/flotsam/xeger/XegerUtilsTest.java diff --git a/accurest-converters/src/main/groovy/nl/flotsam/xeger/Xeger.java b/accurest-converters/src/main/groovy/nl/flotsam/xeger/Xeger.java new file mode 100644 index 0000000000..8fd7d28628 --- /dev/null +++ b/accurest-converters/src/main/groovy/nl/flotsam/xeger/Xeger.java @@ -0,0 +1,114 @@ +/** + * Copyright 2009 Wilfred Springer + * Copyright 2012 Jason Pell + * Copyright 2013 Antonio García-Domínguez + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.flotsam.xeger; + +import dk.brics.automaton.Automaton; +import dk.brics.automaton.RegExp; +import dk.brics.automaton.State; +import dk.brics.automaton.Transition; + +import java.util.List; +import java.util.Random; + +/** + * An object that will generate text from a regular expression. In a way, it's the opposite of a regular expression + * matcher: an instance of this class will produce text that is guaranteed to match the regular expression passed in. + */ +public class Xeger { + + private final Automaton automaton; + private Random random; + + /** + * Constructs a new instance, accepting the regular expression and the randomizer. + * + * @param regex The regular expression. (Not null.) + * @param random The object that will randomize the way the String is generated. (Not null.) + * @throws IllegalArgumentException If the regular expression is invalid. + */ + public Xeger(String regex, Random random) { + assert regex != null; + assert random != null; + this.automaton = new RegExp(regex).toAutomaton(); + this.random = random; + } + + /** + * As {@link nl.flotsam.xeger.Xeger#Xeger(String, java.util.Random)}, creating a {@link java.util.Random} instance + * implicityly. + * + * @param regex as string + */ + public Xeger(String regex) { + this(regex, new Random()); + } + + /** + * Generates a random String that is guaranteed to match the regular expression passed to the constructor. + * @return generated regexp + */ + public String generate() { + StringBuilder builder = new StringBuilder(); + generate(builder, automaton.getInitialState()); + return builder.toString(); + } + + private void generate(StringBuilder builder, State state) { + List transitions = state.getSortedTransitions(false); + if (transitions.size() == 0) { + assert state.isAccept(); + return; + } + int nroptions = state.isAccept() ? transitions.size() : transitions.size() - 1; + int option = Xeger.getRandomInt(0, nroptions, random); + if (state.isAccept() && option == 0) { // 0 is considered stop + return; + } + // Moving on to next transition + Transition transition = transitions.get(option - (state.isAccept() ? 1 : 0)); + appendChoice(builder, transition); + generate(builder, transition.getDest()); + } + + private void appendChoice(StringBuilder builder, Transition transition) { + char c = (char) Xeger.getRandomInt(transition.getMin(), transition.getMax(), random); + builder.append(c); + } + + public Random getRandom() { + return random; + } + + public void setRandom(Random random) { + this.random = random; + } + + /** + * Generates a random number within the given bounds. + * + * @param min The minimum number (inclusive). + * @param max The maximum number (inclusive). + * @param random The object used as the randomizer. + * @return A random number in the given range. + */ + static int getRandomInt(int min, int max, Random random) { + // Use random.nextInt as it guarantees a uniform distribution + int maxForRandom=max-min+1; + return random.nextInt(maxForRandom) + min; + } +} \ No newline at end of file diff --git a/accurest-converters/src/test/groovy/nl/flotsam/xeger/XegerTest.java b/accurest-converters/src/test/groovy/nl/flotsam/xeger/XegerTest.java new file mode 100644 index 0000000000..f60e4bf5db --- /dev/null +++ b/accurest-converters/src/test/groovy/nl/flotsam/xeger/XegerTest.java @@ -0,0 +1,64 @@ +/** + * Copyright 2009 Wilfred Springer + * Copyright 2012 Jason Pell + * Copyright 2013 Antonio García-Domínguez + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.flotsam.xeger; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class XegerTest { + + @Test + public void shouldGenerateTextCorrectly() { + String regex = "[ab]{4,6}c"; + Xeger generator = new Xeger(regex); + for (int i = 0; i < 100; i++) { + String text = generator.generate(); + assertTrue(text.matches(regex)); + } + } + + @Test + public void testRepeatableRegex() { + for (int x = 0; x < 1000; x++) { + Xeger generator = new Xeger("[ab]{4,6}c", new Random(1000)); + Xeger generator2 = new Xeger("[ab]{4,6}c", new Random(1000)); + + List firstRegexList = generateRegex(generator, 100); + List secondRegexList = generateRegex(generator2, 100); + + for (int i = 0; i < firstRegexList.size(); i++) { + assertEquals("Index mismatch: " + i, firstRegexList.get(i), + secondRegexList.get(i)); + } + } + } + + private List generateRegex(Xeger generator, int count) { + List regexList = new ArrayList(); + for (int i = 0; i < count; i++) { + regexList.add(generator.generate()); + } + return regexList; + } +} \ No newline at end of file diff --git a/accurest-converters/src/test/groovy/nl/flotsam/xeger/XegerUtilsTest.java b/accurest-converters/src/test/groovy/nl/flotsam/xeger/XegerUtilsTest.java new file mode 100644 index 0000000000..ccabfeb54d --- /dev/null +++ b/accurest-converters/src/test/groovy/nl/flotsam/xeger/XegerUtilsTest.java @@ -0,0 +1,39 @@ +/** + * Copyright 2009 Wilfred Springer + * Copyright 2012 Jason Pell + * Copyright 2013 Antonio García-Domínguez + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.flotsam.xeger; + +import org.hamcrest.Matchers; +import org.junit.Test; + +import java.util.Random; + +import static org.junit.Assert.assertThat; + +public class XegerUtilsTest { + + @Test + public void shouldGenerateRandomNumberCorrectly() { + Random random = new Random(); + for (int i = 0; i < 100; i++) { + int number = Xeger.getRandomInt(3, 7, random); + assertThat(number, Matchers.greaterThanOrEqualTo(3)); + assertThat(number, Matchers.lessThanOrEqualTo(7)); + } + } + +} \ No newline at end of file diff --git a/build.gradle b/build.gradle index 783681eebd..8ada1b5a2a 100644 --- a/build.gradle +++ b/build.gradle @@ -47,9 +47,6 @@ subprojects { repositories { mavenLocal() mavenCentral() - maven { - url "https://jitpack.io" - } } //Dependencies in all subprojects - http://solidsoft.wordpress.com/2014/11/13/gradle-tricks-display-dependencies-for-all-subprojects-in-multi-project-build/ @@ -101,8 +98,9 @@ project(':accurest-converters') { compile project(':accurest-core') compile 'org.apache.commons:commons-lang3:3.3.2' compile 'commons-io:commons-io:[2.4,)' - compile 'com.github.marcingrzejszczak:xeger:20130128' + compile 'dk.brics.automaton:automaton:1.11-8' // needed for Xeger testCompile 'com.github.tomakehurst:wiremock:1.53' + testCompile 'org.hamcrest:hamcrest-all:1.3' } } From 1142b68123eac6346e7adf2f22f2539441281db6 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Tue, 12 May 2015 23:53:32 +0200 Subject: [PATCH 009/119] Added missing Javadoc entry about bundling reason --- .../src/main/groovy/nl/flotsam/xeger/Xeger.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/accurest-converters/src/main/groovy/nl/flotsam/xeger/Xeger.java b/accurest-converters/src/main/groovy/nl/flotsam/xeger/Xeger.java index 8fd7d28628..9bd42a674a 100644 --- a/accurest-converters/src/main/groovy/nl/flotsam/xeger/Xeger.java +++ b/accurest-converters/src/main/groovy/nl/flotsam/xeger/Xeger.java @@ -14,6 +14,10 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * + * The class is bundled together with our code because it has not been + * released to any central repository. + * */ package nl.flotsam.xeger; From 3be6c8e889c4e509240b94edf05a7dc7eece76e5 Mon Sep 17 00:00:00 2001 From: Jakub Kubrynski Date: Wed, 13 May 2015 00:04:34 +0200 Subject: [PATCH 010/119] Release version: 0.6.2 [ci skip] From ad2c8b23e389754998207545fc7c9d610c8e1528 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Thu, 14 May 2015 00:35:00 +0200 Subject: [PATCH 011/119] WIP on the Wiremock stubs fix with bodyPattern matches --- .../dsl/WiremockRequestStubStrategy.groovy | 4 +- .../accurest/dsl/WiremockGroovyDslSpec.groovy | 63 ++++++++++++++++++- 2 files changed, 65 insertions(+), 2 deletions(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy index 97a7e942ba..36ba00ed72 100755 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy @@ -39,7 +39,9 @@ class WiremockRequestStubStrategy extends BaseWiremockStubStrategy { return [:] } if (containsRegex(body)) { - return [bodyPatterns: [[matches: parseBody(JsonConverter.transformValues(body, { it.toString() }))]]] + return [bodyPatterns: [[matches: parseBody(JsonConverter.transformValues(body, { + it instanceof Pattern ? it.toString() : it + }))]]] } return [bodyPatterns: [[equalTo: parseBody(body)]]] } diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy index b813ee11ef..fddf781fd3 100755 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy @@ -148,7 +148,6 @@ class WiremockGroovyDslSpec extends WiremockSpec { stubMappingIsValidWiremockStub(wiremockStub) } - def 'should convert groovy dsl stub with regexp Body as String to wiremock stub for the client side'() { given: GroovyDsl groovyDsl = GroovyDsl.make { @@ -201,6 +200,68 @@ class WiremockGroovyDslSpec extends WiremockSpec { stubMappingIsValidWiremockStub(wiremockStub) } + def 'should convert groovy dsl stub with a regexp and an integer in request body'() { + given: + GroovyDsl groovyDsl = GroovyDsl.make { + request { + method 'PUT' + url '/fraudcheck' + body(""" + { + "clientPesel":"${value(client(regex('[0-9]{10}')), server('1234567890'))}", + "loanAmount":123.123 + } + """ + ) + headers { + header('Content-Type', 'application/vnd.fraud.v1+json') + } + + } + response { + status 200 + body( + fraudCheckStatus: "OK", + rejectionReason: $(client(null), server(execute('assertThatRejectionReasonIsNull($it)'))) + ) + headers { + header('Content-Type': 'application/vnd.fraud.v1+json') + } + } + + } + when: + String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockClientStub() + then: + new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText(''' +{ + "request": { + "method": "PUT", + "headers": { + "Content-Type": { + "equalTo": "application/vnd.fraud.v1+json" + } + }, + "url": "/fraudcheck", + "bodyPatterns": [ + { + "matches": "\\\\{\\"clientPesel\\":\\"[0-9]{10}\\",\\"loanAmount\\":123.123\\\\}" + } + ] + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/vnd.fraud.v1+json" + }, + "body": "{\\"fraudCheckStatus\\":\\"OK\\",\\"rejectionReason\\":null}" + } +} +''') + and: + stubMappingIsValidWiremockStub(wiremockStub) + } + def "should generate stub with GET"() { given: From 6e6dda3994b9ddabb4b1c960dcc1c6f6b5c9d8eb Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Thu, 14 May 2015 16:58:05 +0200 Subject: [PATCH 012/119] Added tests, added additional checks (for regexps), added presentation examples --- .../dsl/WiremockRequestStubStrategy.groovy | 25 ++- .../internal/JsonStructureConverter.groovy | 32 ++++ .../accurest/dsl/WiremockGroovyDslSpec.groovy | 2 +- .../codearte/accurest/dsl/WiremockSpec.groovy | 7 +- .../plugin/PresentationExampleSpec.groovy | 21 +++ .../presentationExample/build.gradle | 94 ++++++++++ .../shouldMarkClientAsFraud.groovy | 27 +++ .../shouldMarkClientAsNotFraud.groovy | 28 +++ .../frauddetection/Application.java | 17 ++ .../FraudDetectionController.java | 39 +++++ .../frauddetection/model/FraudCheck.java | 29 ++++ .../model/FraudCheckResult.java | 32 ++++ .../model/FraudCheckStatus.java | 5 + .../src/main/resources/application.yml | 1 + .../com/blogspot/toomuchcoding/MvcSpec.groovy | 15 ++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 50514 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + .../presentationExample/gradlew | 164 ++++++++++++++++++ .../presentationExample/gradlew.bat | 90 ++++++++++ .../loanApplicationService/mappings/.gitkeep | 0 .../frauddetection/Application.java | 17 ++ .../LoanApplicationService.java | 62 +++++++ .../frauddetection/model/Client.java | 14 ++ .../model/FraudCheckStatus.java | 5 + .../model/FraudServiceRequest.java | 34 ++++ .../model/FraudServiceResponse.java | 27 +++ .../frauddetection/model/LoanApplication.java | 36 ++++ .../model/LoanApplicationResult.java | 32 ++++ .../model/LoanApplicationStatus.java | 5 + .../src/main/resources/application.yml | 1 + .../LoanApplicationServiceSpec.groovy | 50 ++++++ .../shouldMarkClientAsFraud.json | 23 +++ .../shouldMarkClientAsNotFraud.json | 23 +++ .../presentationExample/settings.gradle | 2 + build.gradle | 1 + 35 files changed, 960 insertions(+), 6 deletions(-) create mode 100644 accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/JsonStructureConverter.groovy create mode 100755 accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/PresentationExampleSpec.groovy create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/build.gradle create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsFraud.groovy create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.groovy create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/Application.java create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/FraudDetectionController.java create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheck.java create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckResult.java create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckStatus.java create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/main/resources/application.yml create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/test/groovy/com/blogspot/toomuchcoding/MvcSpec.groovy create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/gradle/wrapper/gradle-wrapper.jar create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/gradle/wrapper/gradle-wrapper.properties create mode 100755 accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/gradlew create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/gradlew.bat create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/mappings/.gitkeep create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/Application.java create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/LoanApplicationService.java create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/Client.java create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckStatus.java create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudServiceRequest.java create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudServiceResponse.java create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplication.java create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplicationResult.java create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplicationStatus.java create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/resources/application.yml create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/test/groovy/com/blogspot/toomuchcoding/LoanApplicationServiceSpec.groovy create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsFraud.json create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.json create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/settings.gradle diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy index 36ba00ed72..d6854255d9 100755 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy @@ -3,10 +3,12 @@ import groovy.transform.PackageScope import groovy.transform.TypeChecked import io.codearte.accurest.dsl.internal.ClientRequest import io.codearte.accurest.dsl.internal.Request -import io.codearte.accurest.util.JsonConverter import java.util.regex.Pattern +import static io.codearte.accurest.dsl.internal.JsonStructureConverter.TEMPORARY_PATTERN_HOLDER +import static io.codearte.accurest.dsl.internal.JsonStructureConverter.convertJsonStructureToObjectUnderstandingStructure + @TypeChecked @PackageScope class WiremockRequestStubStrategy extends BaseWiremockStubStrategy { @@ -39,13 +41,28 @@ class WiremockRequestStubStrategy extends BaseWiremockStubStrategy { return [:] } if (containsRegex(body)) { - return [bodyPatterns: [[matches: parseBody(JsonConverter.transformValues(body, { - it instanceof Pattern ? it.toString() : it - }))]]] + return [bodyPatterns: [[matches: parseBody(convertJsonStructureToObjectUnderstandingStructure(body, + { it instanceof Pattern }, + { String json -> json.collect { + switch(it) { + case ('{'): return '\\{' + case ('}'): return '\\}' + default: return it + } + } .join('') + }, + { LinkedList list, String json -> + return json.replaceAll(TEMPORARY_PATTERN_HOLDER, { String a, String[] b -> list.pop() }) + } + ))]]] } return [bodyPatterns: [[equalTo: parseBody(body)]]] } + protected String parseBody(Object body) { + return body + } + boolean containsRegex(Object bodyObject) { String bodyString = bodyObject as String return (bodyString =~ /\^.*\$/).find() diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/JsonStructureConverter.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/JsonStructureConverter.groovy new file mode 100644 index 0000000000..f5860bfaad --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/JsonStructureConverter.groovy @@ -0,0 +1,32 @@ +package io.codearte.accurest.dsl.internal + +import groovy.json.JsonOutput +import groovy.transform.CompileStatic +import io.codearte.accurest.util.JsonConverter + +import java.util.regex.Pattern + +@CompileStatic +class JsonStructureConverter { + + public static final String TEMPORARY_PLACEHOLDER = '###PLACEHOLDER###' + public static final Pattern TEMPORARY_PATTERN_HOLDER = Pattern.compile(TEMPORARY_PLACEHOLDER) + + static Object convertJsonStructureToObjectUnderstandingStructure(Object parsedJson, + Closure retrievePlaceholders, + Closure performAdditionalLogicOnSerializedJson, + Closure convertSerializedJsonToSth) { + LinkedList queue = new LinkedList<>() + def transformedJson = JsonConverter.transformValues(parsedJson, { + if(retrievePlaceholders(it)) { + queue.push(it) + return TEMPORARY_PLACEHOLDER + } + return it + }) + String jsonAsString = JsonOutput.toJson(transformedJson) + String transformedJsonAsString = performAdditionalLogicOnSerializedJson(jsonAsString) + return convertSerializedJsonToSth(queue, transformedJsonAsString) + } + +} diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy index fddf781fd3..f5b61625e3 100755 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy @@ -183,7 +183,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { "urlPattern": "/[0-9]{2}", "bodyPatterns": [ { - "matches":"{\\"personalId\\":\\"^[0-9]{11}$\\"}" + "matches":"\\\\{\\"personalId\\":\\"^[0-9]{11}$\\"\\\\}" } ] }, diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockSpec.groovy index 2a6237d89d..796e603c11 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockSpec.groovy @@ -3,10 +3,15 @@ package io.codearte.accurest.dsl import com.github.tomakehurst.wiremock.stubbing.StubMapping import spock.lang.Specification +import java.util.regex.Pattern + class WiremockSpec extends Specification { void stubMappingIsValidWiremockStub(String mappingDefinition) { - StubMapping.buildFrom(mappingDefinition) + StubMapping stubMapping = StubMapping.buildFrom(mappingDefinition) + stubMapping.request.bodyPatterns.findAll { it.matches }.every { + Pattern.compile(it.matches) + } } } diff --git a/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/PresentationExampleSpec.groovy b/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/PresentationExampleSpec.groovy new file mode 100755 index 0000000000..3cc7845074 --- /dev/null +++ b/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/PresentationExampleSpec.groovy @@ -0,0 +1,21 @@ +package io.codearte.accurest.plugin + +import nebula.test.IntegrationSpec +import spock.lang.Stepwise + +@Stepwise +class PresentationExampleSpec extends IntegrationSpec { + + void setup() { + copyResources("functionalTest/presentationExample", "") + runTasksSuccessfully('clean') //delete accidental output when previously importing SimpleBoot into Idea to tweak it + } + + def "should pass basic flow"() { + given: + assert fileExists('build.gradle') + expect: + runTasksSuccessfully('check') + } + +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/build.gradle b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/build.gradle new file mode 100644 index 0000000000..9bf00d6c55 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/build.gradle @@ -0,0 +1,94 @@ +buildscript { + repositories { + mavenCentral() + mavenLocal() + } + dependencies { + classpath("org.springframework.boot:spring-boot-gradle-plugin:1.2.1.RELEASE") + classpath 'io.codearte.accurest:accurest-gradle-plugin:0.6.2' + } +} + +ext { + spockVersion = '0.7-groovy-2.0' + restAssuredVersion = '2.4.0' + + accurestStubsBaseDirectory = 'src/test/resources/stubs' +} + +subprojects { + apply plugin: 'groovy' + + + repositories { + mavenCentral() + mavenLocal() + } + + dependencies { + testCompile "org.codehaus.groovy:groovy-all:2.3.7" + testCompile "org.spockframework:spock-core:$spockVersion" + testCompile("junit:junit:4.12") + testCompile('com.github.tomakehurst:wiremock:1.52') { + exclude group: 'org.mortbay.jetty', module: 'servlet-api' + } + } +} + +configure([project(':fraudDetectionService'), project(':loanApplicationService')]) { + apply plugin: 'spring-boot' + apply plugin: 'accurest' + + ext { + wiremockStubsOutputDirRoot = file("${project.buildDir}/production/${project.name}-stubs/") + wiremockStubsOutputDir = new File(wiremockStubsOutputDirRoot, 'mappings/') + } + + accurest { + targetFramework = 'Spock' + testMode = 'MockMvc' + baseClassForTests = 'com.blogspot.toomuchcoding.MvcSpec' + contractsDslDir = file("${project.projectDir.absolutePath}/mappings/") + generatedTestSourcesDir = file("${project.buildDir}/generated-sources/") + stubsOutputDir = wiremockStubsOutputDir + } + + jar { + version = '0.0.1' + } + + dependencies { + compile("org.springframework.boot:spring-boot-starter-web") { + exclude module: "spring-boot-starter-tomcat" + } + compile("org.springframework.boot:spring-boot-starter-jetty") + compile("org.springframework.boot:spring-boot-starter-actuator") + + testRuntime "org.spockframework:spock-spring:$spockVersion" + testCompile "org.springframework:spring-test" + testCompile "com.jayway.restassured:rest-assured:$restAssuredVersion" + testCompile "com.jayway.restassured:spring-mock-mvc:$restAssuredVersion" + } + + task cleanup(type: Delete) { + delete 'src/test/resources/mappings', 'src/test/resources/stubs' + } + + clean.dependsOn('cleanup') + +} + +configure(project(':fraudDetectionService')) { + test.dependsOn('generateWiremockClientStubs') +} + +configure(project(':loanApplicationService')) { + + task copyCollaboratorStubs(type: Copy) { + File fraudBuildDir = project(':fraudDetectionService').buildDir + from(new File(fraudBuildDir, "/production/${project(':fraudDetectionService').name}-stubs/")) + into "src/test/resources/" + } + + generateAccurest.dependsOn('copyCollaboratorStubs') +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsFraud.groovy b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsFraud.groovy new file mode 100644 index 0000000000..a47dff32e4 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsFraud.groovy @@ -0,0 +1,27 @@ +io.codearte.accurest.dsl.GroovyDsl.make { + request { + method """PUT""" + url """/fraudcheck""" + body(""" + { + "clientPesel":"${value(client(regex('[0-9]{10}')), server('1234567890'))}", + "loanAmount":99999} + """ + ) + headers { + header("""Content-Type""", """application/vnd.fraud.v1+json""") + } + + } + response { + status 200 + body( """{ + "fraudCheckStatus": "${value(client('FRAUD'), server(regex('[A-Z]{5}')))}", + "rejectionReason": "Amount too high" +}""") + headers { + header('Content-Type': 'application/vnd.fraud.v1+json') + } + } + +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.groovy b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.groovy new file mode 100644 index 0000000000..fa8cd88ade --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.groovy @@ -0,0 +1,28 @@ +io.codearte.accurest.dsl.GroovyDsl.make { + request { + method 'PUT' + url '/fraudcheck' + body(""" + { + "clientPesel":"${value(client(regex('[0-9]{10}')), server('1234567890'))}", + "loanAmount":123.123 + } + """ + ) + headers { + header('Content-Type', 'application/vnd.fraud.v1+json') + } + + } + response { + status 200 + body( + fraudCheckStatus: "OK", + rejectionReason: $(client(null), server(execute('assertThatRejectionReasonIsNull($it)'))) + ) + headers { + header('Content-Type': 'application/vnd.fraud.v1+json') + } + } + +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/Application.java b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/Application.java new file mode 100644 index 0000000000..5a1a60244e --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/Application.java @@ -0,0 +1,17 @@ +package com.blogspot.toomuchcoding.frauddetection; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +@Configuration +@EnableAutoConfiguration +@ComponentScan +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/FraudDetectionController.java b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/FraudDetectionController.java new file mode 100644 index 0000000000..e264462cce --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/FraudDetectionController.java @@ -0,0 +1,39 @@ +package com.blogspot.toomuchcoding.frauddetection; + +import com.blogspot.toomuchcoding.frauddetection.model.FraudCheck; +import com.blogspot.toomuchcoding.frauddetection.model.FraudCheckResult; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.math.BigDecimal; + +import static com.blogspot.toomuchcoding.frauddetection.model.FraudCheckStatus.FRAUD; +import static com.blogspot.toomuchcoding.frauddetection.model.FraudCheckStatus.OK; +import static org.springframework.web.bind.annotation.RequestMethod.PUT; + +@RestController +public class FraudDetectionController { + + private static final String FRAUD_SERVICE_JSON_VERSION_1 = "application/vnd.fraud.v1+json"; + private static final String NO_REASON = null; + private static final String AMOUNT_TOO_HIGH = "Amount too high"; + private static final BigDecimal MAX_AMOUNT = new BigDecimal("5000"); + + @RequestMapping( + value = "/fraudcheck", + method = PUT, + consumes = FRAUD_SERVICE_JSON_VERSION_1, + produces = FRAUD_SERVICE_JSON_VERSION_1) + public FraudCheckResult fraudCheck(@RequestBody FraudCheck fraudCheck) { + if (amountGreaterThanThreshold(fraudCheck)) { + return new FraudCheckResult(FRAUD, AMOUNT_TOO_HIGH); + } + return new FraudCheckResult(OK, NO_REASON); + } + + private boolean amountGreaterThanThreshold(FraudCheck fraudCheck) { + return MAX_AMOUNT.compareTo(fraudCheck.getLoanAmount()) < 0; + } + +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheck.java b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheck.java new file mode 100644 index 0000000000..77471aee19 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheck.java @@ -0,0 +1,29 @@ +package com.blogspot.toomuchcoding.frauddetection.model; + +import java.math.BigDecimal; + +public class FraudCheck { + + private String clientPesel; + + private BigDecimal loanAmount; + + public FraudCheck() { + } + + public String getClientPesel() { + return clientPesel; + } + + public void setClientPesel(String clientPesel) { + this.clientPesel = clientPesel; + } + + public BigDecimal getLoanAmount() { + return loanAmount; + } + + public void setLoanAmount(BigDecimal loanAmount) { + this.loanAmount = loanAmount; + } +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckResult.java b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckResult.java new file mode 100644 index 0000000000..28efc573f5 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckResult.java @@ -0,0 +1,32 @@ +package com.blogspot.toomuchcoding.frauddetection.model; + +public class FraudCheckResult { + + private FraudCheckStatus fraudCheckStatus; + + private String rejectionReason; + + public FraudCheckResult() { + } + + public FraudCheckResult(FraudCheckStatus fraudCheckStatus, String rejectionReason) { + this.fraudCheckStatus = fraudCheckStatus; + this.rejectionReason = rejectionReason; + } + + public FraudCheckStatus getFraudCheckStatus() { + return fraudCheckStatus; + } + + public void setFraudCheckStatus(FraudCheckStatus fraudCheckStatus) { + this.fraudCheckStatus = fraudCheckStatus; + } + + public String getRejectionReason() { + return rejectionReason; + } + + public void setRejectionReason(String rejectionReason) { + this.rejectionReason = rejectionReason; + } +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckStatus.java b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckStatus.java new file mode 100644 index 0000000000..b87c365d51 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckStatus.java @@ -0,0 +1,5 @@ +package com.blogspot.toomuchcoding.frauddetection.model; + +public enum FraudCheckStatus { + OK, FRAUD +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/main/resources/application.yml b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/main/resources/application.yml new file mode 100644 index 0000000000..a30a91f034 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/main/resources/application.yml @@ -0,0 +1 @@ +server.port=8085 \ No newline at end of file diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/test/groovy/com/blogspot/toomuchcoding/MvcSpec.groovy b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/test/groovy/com/blogspot/toomuchcoding/MvcSpec.groovy new file mode 100644 index 0000000000..bcb6ef1579 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/test/groovy/com/blogspot/toomuchcoding/MvcSpec.groovy @@ -0,0 +1,15 @@ +package com.blogspot.toomuchcoding + +import com.blogspot.toomuchcoding.frauddetection.FraudDetectionController +import com.jayway.restassured.module.mockmvc.RestAssuredMockMvc +import spock.lang.Specification + +class MvcSpec extends Specification { + def setup() { + RestAssuredMockMvc.standaloneSetup(new FraudDetectionController()) + } + + void assertThatRejectionReasonIsNull(def rejectionReason) { + assert !rejectionReason + } +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/gradle/wrapper/gradle-wrapper.jar b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..667288ad6c2b3b87c990ece1267e56f0bcbf3622 GIT binary patch literal 50514 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 \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/gradlew.bat b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/gradlew.bat new file mode 100644 index 0000000000..8a0b282aa6 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/mappings/.gitkeep b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/mappings/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/Application.java b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/Application.java new file mode 100644 index 0000000000..5a1a60244e --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/Application.java @@ -0,0 +1,17 @@ +package com.blogspot.toomuchcoding.frauddetection; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +@Configuration +@EnableAutoConfiguration +@ComponentScan +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/LoanApplicationService.java b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/LoanApplicationService.java new file mode 100644 index 0000000000..82476a3a46 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/LoanApplicationService.java @@ -0,0 +1,62 @@ +package com.blogspot.toomuchcoding.frauddetection; + +import com.blogspot.toomuchcoding.frauddetection.model.FraudCheckStatus; +import com.blogspot.toomuchcoding.frauddetection.model.FraudServiceRequest; +import com.blogspot.toomuchcoding.frauddetection.model.FraudServiceResponse; +import com.blogspot.toomuchcoding.frauddetection.model.LoanApplication; +import com.blogspot.toomuchcoding.frauddetection.model.LoanApplicationResult; +import com.blogspot.toomuchcoding.frauddetection.model.LoanApplicationStatus; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +@Service +public class LoanApplicationService { + + private static final String FRAUD_SERVICE_JSON_VERSION_1 = + "application/vnd.fraud.v1+json"; + + private final RestTemplate restTemplate; + + public LoanApplicationService() { + this.restTemplate = new RestTemplate(); + } + + public LoanApplicationResult loanApplication(LoanApplication loanApplication) { + FraudServiceRequest request = + new FraudServiceRequest(loanApplication); + + FraudServiceResponse response = + sendRequestToFraudDetectionService(request); + + return buildResponseFromFraudResult(response); + } + + private FraudServiceResponse sendRequestToFraudDetectionService( + FraudServiceRequest request) { + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.add(HttpHeaders.CONTENT_TYPE, FRAUD_SERVICE_JSON_VERSION_1); + + ResponseEntity response = + restTemplate.exchange("http://localhost:8080/fraudcheck", HttpMethod.PUT, + new HttpEntity<>(request, httpHeaders), + FraudServiceResponse.class); + + return response.getBody(); + } + + private LoanApplicationResult buildResponseFromFraudResult(FraudServiceResponse response) { + LoanApplicationStatus applicationStatus = null; + if (FraudCheckStatus.OK == response.getFraudCheckStatus()) { + applicationStatus = LoanApplicationStatus.LOAN_APPLIED; + } else if (FraudCheckStatus.FRAUD == response.getFraudCheckStatus()) { + applicationStatus = LoanApplicationStatus.LOAN_APPLICATION_REJECTED; + } + + return new LoanApplicationResult(applicationStatus, response.getRejectionReason()); + } + +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/Client.java b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/Client.java new file mode 100644 index 0000000000..5e91273eda --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/Client.java @@ -0,0 +1,14 @@ +package com.blogspot.toomuchcoding.frauddetection.model; + +public class Client { + + private String pesel; + + public String getPesel() { + return pesel; + } + + public void setPesel(String pesel) { + this.pesel = pesel; + } +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckStatus.java b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckStatus.java new file mode 100644 index 0000000000..b87c365d51 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckStatus.java @@ -0,0 +1,5 @@ +package com.blogspot.toomuchcoding.frauddetection.model; + +public enum FraudCheckStatus { + OK, FRAUD +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudServiceRequest.java b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudServiceRequest.java new file mode 100644 index 0000000000..ac595998bc --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudServiceRequest.java @@ -0,0 +1,34 @@ +package com.blogspot.toomuchcoding.frauddetection.model; + +import java.math.BigDecimal; + +public class FraudServiceRequest { + + private String clientPesel; + + private BigDecimal loanAmount; + + public FraudServiceRequest() { + } + + public FraudServiceRequest(LoanApplication loanApplication) { + this.clientPesel = loanApplication.getClient().getPesel(); + this.loanAmount = loanApplication.getAmount(); + } + + public String getClientPesel() { + return clientPesel; + } + + public void setClientPesel(String clientPesel) { + this.clientPesel = clientPesel; + } + + public BigDecimal getLoanAmount() { + return loanAmount; + } + + public void setLoanAmount(BigDecimal loanAmount) { + this.loanAmount = loanAmount; + } +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudServiceResponse.java b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudServiceResponse.java new file mode 100644 index 0000000000..9f3353ecbf --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudServiceResponse.java @@ -0,0 +1,27 @@ +package com.blogspot.toomuchcoding.frauddetection.model; + +public class FraudServiceResponse { + + private FraudCheckStatus fraudCheckStatus; + + private String rejectionReason; + + public FraudServiceResponse() { + } + + public FraudCheckStatus getFraudCheckStatus() { + return fraudCheckStatus; + } + + public void setFraudCheckStatus(FraudCheckStatus fraudCheckStatus) { + this.fraudCheckStatus = fraudCheckStatus; + } + + public String getRejectionReason() { + return rejectionReason; + } + + public void setRejectionReason(String rejectionReason) { + this.rejectionReason = rejectionReason; + } +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplication.java b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplication.java new file mode 100644 index 0000000000..816087988b --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplication.java @@ -0,0 +1,36 @@ +package com.blogspot.toomuchcoding.frauddetection.model; + +import java.math.BigDecimal; + +public class LoanApplication { + + private Client client; + + private BigDecimal amount; + + private String loanApplicationId; + + public Client getClient() { + return client; + } + + public void setClient(Client client) { + this.client = client; + } + + public BigDecimal getAmount() { + return amount; + } + + public void setAmount(BigDecimal amount) { + this.amount = amount; + } + + public String getLoanApplicationId() { + return loanApplicationId; + } + + public void setLoanApplicationId(String loanApplicationId) { + this.loanApplicationId = loanApplicationId; + } +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplicationResult.java b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplicationResult.java new file mode 100644 index 0000000000..523f4f2ea3 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplicationResult.java @@ -0,0 +1,32 @@ +package com.blogspot.toomuchcoding.frauddetection.model; + +public class LoanApplicationResult { + + private LoanApplicationStatus loanApplicationStatus; + + private String rejectionReason; + + public LoanApplicationResult() { + } + + public LoanApplicationResult(LoanApplicationStatus loanApplicationStatus, String rejectionReason) { + this.loanApplicationStatus = loanApplicationStatus; + this.rejectionReason = rejectionReason; + } + + public LoanApplicationStatus getLoanApplicationStatus() { + return loanApplicationStatus; + } + + public void setLoanApplicationStatus(LoanApplicationStatus loanApplicationStatus) { + this.loanApplicationStatus = loanApplicationStatus; + } + + public String getRejectionReason() { + return rejectionReason; + } + + public void setRejectionReason(String rejectionReason) { + this.rejectionReason = rejectionReason; + } +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplicationStatus.java b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplicationStatus.java new file mode 100644 index 0000000000..7f7f86e0ea --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplicationStatus.java @@ -0,0 +1,5 @@ +package com.blogspot.toomuchcoding.frauddetection.model; + +public enum LoanApplicationStatus { + LOAN_APPLIED, LOAN_APPLICATION_REJECTED +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/resources/application.yml b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/resources/application.yml new file mode 100644 index 0000000000..e86bbd0e0f --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/resources/application.yml @@ -0,0 +1 @@ +server.port=8090 \ No newline at end of file diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/test/groovy/com/blogspot/toomuchcoding/LoanApplicationServiceSpec.groovy b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/test/groovy/com/blogspot/toomuchcoding/LoanApplicationServiceSpec.groovy new file mode 100644 index 0000000000..58d17673ce --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/test/groovy/com/blogspot/toomuchcoding/LoanApplicationServiceSpec.groovy @@ -0,0 +1,50 @@ +package com.blogspot.toomuchcoding + +import com.blogspot.toomuchcoding.frauddetection.Application +import com.blogspot.toomuchcoding.frauddetection.LoanApplicationService +import com.blogspot.toomuchcoding.frauddetection.model.Client +import com.blogspot.toomuchcoding.frauddetection.model.LoanApplication +import com.blogspot.toomuchcoding.frauddetection.model.LoanApplicationResult +import com.blogspot.toomuchcoding.frauddetection.model.LoanApplicationStatus +import com.github.tomakehurst.wiremock.junit.WireMockClassRule +import org.junit.ClassRule +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.SpringApplicationContextLoader +import org.springframework.test.context.ContextConfiguration +import spock.lang.Shared +import spock.lang.Specification + +@ContextConfiguration(loader = SpringApplicationContextLoader, classes = Application) +class LoanApplicationServiceSpec extends Specification { + + @ClassRule + @Shared + WireMockClassRule wireMockRule = new WireMockClassRule() + + @Autowired + LoanApplicationService sut + + def 'should successfully apply for loan'() { + given: + LoanApplication application = + new LoanApplication(client: new Client(pesel: '1234567890'), amount: 123.123) + when: + LoanApplicationResult loanApplication = sut.loanApplication(application) + then: + loanApplication.loanApplicationStatus == LoanApplicationStatus.LOAN_APPLIED + loanApplication.rejectionReason == null + } + + def 'should be rejected due to abnormal loan amount'() { + given: + LoanApplication application = + new LoanApplication(client: new Client(pesel: '1234567890'), amount: 99_999) + when: + LoanApplicationResult loanApplication = sut.loanApplication(application) + then: + loanApplication.loanApplicationStatus == LoanApplicationStatus.LOAN_APPLICATION_REJECTED + loanApplication.rejectionReason == 'Amount too high' + } + + +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsFraud.json b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsFraud.json new file mode 100644 index 0000000000..610b4ae1b1 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsFraud.json @@ -0,0 +1,23 @@ +{ + "request": { + "method": "PUT", + "headers": { + "Content-Type": { + "equalTo": "application/vnd.fraud.v1+json" + } + }, + "url": "/fraudcheck", + "bodyPatterns": [ + { + "matches": "{\"clientPesel\":\"[0-9]{10}\",\"loanAmount\":\"99999\"}" + } + ] + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/vnd.fraud.v1+json" + }, + "body": "{\"fraudCheckStatus\":\"FRAUD\",\"rejectionReason\":\"Amount too high\"}" + } +} \ No newline at end of file diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.json b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.json new file mode 100644 index 0000000000..af5792092c --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.json @@ -0,0 +1,23 @@ +{ + "request": { + "method": "PUT", + "headers": { + "Content-Type": { + "equalTo": "application/vnd.fraud.v1+json" + } + }, + "url": "/fraudcheck", + "bodyPatterns": [ + { + "matches": "{\"clientPesel\":\"[0-9]{10}\",\"loanAmount\":\"123.123\"}" + } + ] + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/vnd.fraud.v1+json" + }, + "body": "{\"fraudCheckStatus\":\"OK\",\"rejectionReason\":null}" + } +} \ No newline at end of file diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/settings.gradle b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/settings.gradle new file mode 100644 index 0000000000..6a42a6c7ce --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/settings.gradle @@ -0,0 +1,2 @@ +include ':fraudDetectionService' +include ':loanApplicationService' diff --git a/build.gradle b/build.gradle index 783681eebd..dc8fb0a86a 100644 --- a/build.gradle +++ b/build.gradle @@ -90,6 +90,7 @@ project(':accurest-core') { compile 'org.slf4j:slf4j-api:[1.6.0,)' compile 'org.codehaus.plexus:plexus-utils:3.0.21' compile 'commons-io:commons-io:2.4' + compile 'org.apache.commons:commons-lang3:3.4' testCompile 'cglib:cglib-nodep:2.2' testCompile 'org.objenesis:objenesis:2.1' testCompile 'com.github.tomakehurst:wiremock:1.53' From 74dc41384ee00eadd0d40445d63824c704b486a4 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Fri, 15 May 2015 16:06:47 +0200 Subject: [PATCH 013/119] Changed test project name --- ...nExampleSpec.groovy => SampleProjectSpec.groovy} | 4 ++-- .../build.gradle | 0 .../shouldMarkClientAsFraud.groovy | 0 .../shouldMarkClientAsNotFraud.groovy | 0 .../toomuchcoding/frauddetection/Application.java | 0 .../frauddetection/FraudDetectionController.java | 0 .../frauddetection/model/FraudCheck.java | 0 .../frauddetection/model/FraudCheckResult.java | 0 .../frauddetection/model/FraudCheckStatus.java | 0 .../src/main/resources/application.yml | 0 .../com/blogspot/toomuchcoding/MvcSpec.groovy | 0 .../gradle/wrapper/gradle-wrapper.jar | Bin .../gradle/wrapper/gradle-wrapper.properties | 0 .../{presentationExample => sampleProject}/gradlew | 0 .../gradlew.bat | 0 .../loanApplicationService/mappings/.gitkeep | 0 .../toomuchcoding/frauddetection/Application.java | 0 .../frauddetection/LoanApplicationService.java | 0 .../toomuchcoding/frauddetection/model/Client.java | 0 .../frauddetection/model/FraudCheckStatus.java | 0 .../frauddetection/model/FraudServiceRequest.java | 0 .../frauddetection/model/FraudServiceResponse.java | 0 .../frauddetection/model/LoanApplication.java | 0 .../frauddetection/model/LoanApplicationResult.java | 0 .../frauddetection/model/LoanApplicationStatus.java | 0 .../src/main/resources/application.yml | 0 .../toomuchcoding/LoanApplicationServiceSpec.groovy | 0 .../shouldMarkClientAsFraud.json | 0 .../shouldMarkClientAsNotFraud.json | 0 .../settings.gradle | 0 30 files changed, 2 insertions(+), 2 deletions(-) rename accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/{PresentationExampleSpec.groovy => SampleProjectSpec.groovy} (76%) rename accurest-gradle-plugin/src/test/resources/functionalTest/{presentationExample => sampleProject}/build.gradle (100%) rename accurest-gradle-plugin/src/test/resources/functionalTest/{presentationExample => sampleProject}/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsFraud.groovy (100%) rename accurest-gradle-plugin/src/test/resources/functionalTest/{presentationExample => sampleProject}/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.groovy (100%) rename accurest-gradle-plugin/src/test/resources/functionalTest/{presentationExample => sampleProject}/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/Application.java (100%) rename accurest-gradle-plugin/src/test/resources/functionalTest/{presentationExample => sampleProject}/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/FraudDetectionController.java (100%) rename accurest-gradle-plugin/src/test/resources/functionalTest/{presentationExample => sampleProject}/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheck.java (100%) rename accurest-gradle-plugin/src/test/resources/functionalTest/{presentationExample => sampleProject}/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckResult.java (100%) rename accurest-gradle-plugin/src/test/resources/functionalTest/{presentationExample => sampleProject}/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckStatus.java (100%) rename accurest-gradle-plugin/src/test/resources/functionalTest/{presentationExample => sampleProject}/fraudDetectionService/src/main/resources/application.yml (100%) rename accurest-gradle-plugin/src/test/resources/functionalTest/{presentationExample => sampleProject}/fraudDetectionService/src/test/groovy/com/blogspot/toomuchcoding/MvcSpec.groovy (100%) rename accurest-gradle-plugin/src/test/resources/functionalTest/{presentationExample => sampleProject}/gradle/wrapper/gradle-wrapper.jar (100%) rename accurest-gradle-plugin/src/test/resources/functionalTest/{presentationExample => sampleProject}/gradle/wrapper/gradle-wrapper.properties (100%) rename accurest-gradle-plugin/src/test/resources/functionalTest/{presentationExample => sampleProject}/gradlew (100%) rename accurest-gradle-plugin/src/test/resources/functionalTest/{presentationExample => sampleProject}/gradlew.bat (100%) rename accurest-gradle-plugin/src/test/resources/functionalTest/{presentationExample => sampleProject}/loanApplicationService/mappings/.gitkeep (100%) rename accurest-gradle-plugin/src/test/resources/functionalTest/{presentationExample => sampleProject}/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/Application.java (100%) rename accurest-gradle-plugin/src/test/resources/functionalTest/{presentationExample => sampleProject}/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/LoanApplicationService.java (100%) rename accurest-gradle-plugin/src/test/resources/functionalTest/{presentationExample => sampleProject}/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/Client.java (100%) rename accurest-gradle-plugin/src/test/resources/functionalTest/{presentationExample => sampleProject}/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckStatus.java (100%) rename accurest-gradle-plugin/src/test/resources/functionalTest/{presentationExample => sampleProject}/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudServiceRequest.java (100%) rename accurest-gradle-plugin/src/test/resources/functionalTest/{presentationExample => sampleProject}/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudServiceResponse.java (100%) rename accurest-gradle-plugin/src/test/resources/functionalTest/{presentationExample => sampleProject}/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplication.java (100%) rename accurest-gradle-plugin/src/test/resources/functionalTest/{presentationExample => sampleProject}/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplicationResult.java (100%) rename accurest-gradle-plugin/src/test/resources/functionalTest/{presentationExample => sampleProject}/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplicationStatus.java (100%) rename accurest-gradle-plugin/src/test/resources/functionalTest/{presentationExample => sampleProject}/loanApplicationService/src/main/resources/application.yml (100%) rename accurest-gradle-plugin/src/test/resources/functionalTest/{presentationExample => sampleProject}/loanApplicationService/src/test/groovy/com/blogspot/toomuchcoding/LoanApplicationServiceSpec.groovy (100%) rename accurest-gradle-plugin/src/test/resources/functionalTest/{presentationExample => sampleProject}/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsFraud.json (100%) rename accurest-gradle-plugin/src/test/resources/functionalTest/{presentationExample => sampleProject}/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.json (100%) rename accurest-gradle-plugin/src/test/resources/functionalTest/{presentationExample => sampleProject}/settings.gradle (100%) diff --git a/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/PresentationExampleSpec.groovy b/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/SampleProjectSpec.groovy similarity index 76% rename from accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/PresentationExampleSpec.groovy rename to accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/SampleProjectSpec.groovy index 3cc7845074..e32b8810f4 100755 --- a/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/PresentationExampleSpec.groovy +++ b/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/SampleProjectSpec.groovy @@ -4,10 +4,10 @@ import nebula.test.IntegrationSpec import spock.lang.Stepwise @Stepwise -class PresentationExampleSpec extends IntegrationSpec { +class SampleProjectSpec extends IntegrationSpec { void setup() { - copyResources("functionalTest/presentationExample", "") + copyResources("functionalTest/sampleProject", "") runTasksSuccessfully('clean') //delete accidental output when previously importing SimpleBoot into Idea to tweak it } diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/build.gradle b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/build.gradle similarity index 100% rename from accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/build.gradle rename to accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/build.gradle diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsFraud.groovy b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsFraud.groovy similarity index 100% rename from accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsFraud.groovy rename to accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsFraud.groovy diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.groovy b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.groovy similarity index 100% rename from accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.groovy rename to accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.groovy diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/Application.java b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/Application.java similarity index 100% rename from accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/Application.java rename to accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/Application.java diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/FraudDetectionController.java b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/FraudDetectionController.java similarity index 100% rename from accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/FraudDetectionController.java rename to accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/FraudDetectionController.java diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheck.java b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheck.java similarity index 100% rename from accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheck.java rename to accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheck.java diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckResult.java b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckResult.java similarity index 100% rename from accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckResult.java rename to accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckResult.java diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckStatus.java b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckStatus.java similarity index 100% rename from accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckStatus.java rename to accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckStatus.java diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/main/resources/application.yml b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/fraudDetectionService/src/main/resources/application.yml similarity index 100% rename from accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/main/resources/application.yml rename to accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/fraudDetectionService/src/main/resources/application.yml diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/test/groovy/com/blogspot/toomuchcoding/MvcSpec.groovy b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/fraudDetectionService/src/test/groovy/com/blogspot/toomuchcoding/MvcSpec.groovy similarity index 100% rename from accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/fraudDetectionService/src/test/groovy/com/blogspot/toomuchcoding/MvcSpec.groovy rename to accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/fraudDetectionService/src/test/groovy/com/blogspot/toomuchcoding/MvcSpec.groovy diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/gradle/wrapper/gradle-wrapper.jar b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/gradle/wrapper/gradle-wrapper.jar rename to accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/gradle/wrapper/gradle-wrapper.jar diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/gradle/wrapper/gradle-wrapper.properties b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/gradle/wrapper/gradle-wrapper.properties rename to accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/gradle/wrapper/gradle-wrapper.properties diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/gradlew b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/gradlew similarity index 100% rename from accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/gradlew rename to accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/gradlew diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/gradlew.bat b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/gradlew.bat similarity index 100% rename from accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/gradlew.bat rename to accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/gradlew.bat diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/mappings/.gitkeep b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/loanApplicationService/mappings/.gitkeep similarity index 100% rename from accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/mappings/.gitkeep rename to accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/loanApplicationService/mappings/.gitkeep diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/Application.java b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/Application.java similarity index 100% rename from accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/Application.java rename to accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/Application.java diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/LoanApplicationService.java b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/LoanApplicationService.java similarity index 100% rename from accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/LoanApplicationService.java rename to accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/LoanApplicationService.java diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/Client.java b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/Client.java similarity index 100% rename from accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/Client.java rename to accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/Client.java diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckStatus.java b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckStatus.java similarity index 100% rename from accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckStatus.java rename to accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckStatus.java diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudServiceRequest.java b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudServiceRequest.java similarity index 100% rename from accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudServiceRequest.java rename to accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudServiceRequest.java diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudServiceResponse.java b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudServiceResponse.java similarity index 100% rename from accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudServiceResponse.java rename to accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudServiceResponse.java diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplication.java b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplication.java similarity index 100% rename from accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplication.java rename to accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplication.java diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplicationResult.java b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplicationResult.java similarity index 100% rename from accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplicationResult.java rename to accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplicationResult.java diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplicationStatus.java b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplicationStatus.java similarity index 100% rename from accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplicationStatus.java rename to accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplicationStatus.java diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/resources/application.yml b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/loanApplicationService/src/main/resources/application.yml similarity index 100% rename from accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/main/resources/application.yml rename to accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/loanApplicationService/src/main/resources/application.yml diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/test/groovy/com/blogspot/toomuchcoding/LoanApplicationServiceSpec.groovy b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/loanApplicationService/src/test/groovy/com/blogspot/toomuchcoding/LoanApplicationServiceSpec.groovy similarity index 100% rename from accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/test/groovy/com/blogspot/toomuchcoding/LoanApplicationServiceSpec.groovy rename to accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/loanApplicationService/src/test/groovy/com/blogspot/toomuchcoding/LoanApplicationServiceSpec.groovy diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsFraud.json b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsFraud.json similarity index 100% rename from accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsFraud.json rename to accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsFraud.json diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.json b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.json similarity index 100% rename from accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.json rename to accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.json diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/settings.gradle b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/settings.gradle similarity index 100% rename from accurest-gradle-plugin/src/test/resources/functionalTest/presentationExample/settings.gradle rename to accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/settings.gradle From 7bd9e48f153aa2130c091978485ab69825528886 Mon Sep 17 00:00:00 2001 From: Jakub Kubrynski Date: Sat, 16 May 2015 12:44:23 +0200 Subject: [PATCH 014/119] Ranging dependencies versions --- build.gradle | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index 57f2a9270a..d44130214f 100644 --- a/build.gradle +++ b/build.gradle @@ -55,7 +55,7 @@ subprojects { dependencies { compile localGroovy() - testCompile('org.spockframework:spock-core:0.7-groovy-2.0') { + testCompile('org.spockframework:spock-core:1.0-groovy-2.3') { exclude(group: 'org.codehaus.groovy') } } @@ -85,9 +85,9 @@ subprojects { project(':accurest-core') { dependencies { compile 'org.slf4j:slf4j-api:[1.6.0,)' - compile 'org.codehaus.plexus:plexus-utils:3.0.21' - compile 'commons-io:commons-io:2.4' - compile 'org.apache.commons:commons-lang3:3.4' + compile 'org.codehaus.plexus:plexus-utils:[3.0.0,)' + compile 'commons-io:commons-io:[2.0,)' + compile 'org.apache.commons:commons-lang3:[3.0,)' testCompile 'cglib:cglib-nodep:2.2' testCompile 'org.objenesis:objenesis:2.1' testCompile 'com.github.tomakehurst:wiremock:1.53' @@ -97,8 +97,8 @@ project(':accurest-core') { project(':accurest-converters') { dependencies { compile project(':accurest-core') - compile 'org.apache.commons:commons-lang3:3.3.2' - compile 'commons-io:commons-io:[2.4,)' + compile 'org.apache.commons:commons-lang3:[3.0,)' + compile 'commons-io:commons-io:[2.0,)' compile 'dk.brics.automaton:automaton:1.11-8' // needed for Xeger testCompile 'com.github.tomakehurst:wiremock:1.53' testCompile 'org.hamcrest:hamcrest-all:1.3' @@ -111,7 +111,7 @@ project(':accurest-gradle-plugin') { compile project(':accurest-converters') compile gradleApi() - testCompile('com.netflix.nebula:nebula-test:2.2.0') { + testCompile('com.netflix.nebula:nebula-test:2.2.1') { exclude(group: 'org.spockframework') } } From f5605d75131966f782920dae40850f8dfb18c1bf Mon Sep 17 00:00:00 2001 From: Jakub Kubrynski Date: Sat, 16 May 2015 12:45:34 +0200 Subject: [PATCH 015/119] Release version: 0.6.3 [ci skip] From 078ba57f23ce8a2a9f1a76fc2708e29ab7ea7fc2 Mon Sep 17 00:00:00 2001 From: Jakub Kubrynski Date: Sat, 16 May 2015 12:58:45 +0200 Subject: [PATCH 016/119] Update to Gradle 2.4 --- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index d44130214f..8000423e6c 100644 --- a/build.gradle +++ b/build.gradle @@ -131,5 +131,5 @@ project(':accurest-gradle-plugin') { } task wrapper(type: Wrapper) { - gradleVersion = '2.2.1' + gradleVersion = '2.4' } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a0889f9ef7..8c1b9c3b9f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sun Jan 25 20:45:35 CET 2015 +#Sat May 16 12:50:16 CEST 2015 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-bin.zip From 194d4a034a36871a69b3bb8e3c66386e4682c6a5 Mon Sep 17 00:00:00 2001 From: Denis Stepanov Date: Fri, 5 Jun 2015 12:49:55 +0200 Subject: [PATCH 017/119] Implemented queryParameters matching for urlPattern and urlPath --- .../dsl/WiremockRequestStubStrategy.groovy | 44 ++++++- .../dsl/internal/MatchingStrategy.groovy | 34 +++++ .../dsl/internal/QueryParameter.groovy | 29 +++++ .../dsl/internal/QueryParameters.groovy | 45 +++++++ .../accurest/dsl/internal/Request.groovy | 36 +++++- .../codearte/accurest/dsl/internal/Url.groovy | 20 ++- .../accurest/dsl/internal/UrlPath.groovy | 20 +++ .../accurest/dsl/WiremockGroovyDslSpec.groovy | 118 ++++++++++++++++++ 8 files changed, 337 insertions(+), 9 deletions(-) create mode 100644 accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/MatchingStrategy.groovy create mode 100644 accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameter.groovy create mode 100644 accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameters.groovy create mode 100644 accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/UrlPath.groovy diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy index d6854255d9..aa0b0f6bbc 100755 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy @@ -2,6 +2,9 @@ package io.codearte.accurest.dsl import groovy.transform.PackageScope import groovy.transform.TypeChecked import io.codearte.accurest.dsl.internal.ClientRequest +import io.codearte.accurest.dsl.internal.MatchingStrategy +import io.codearte.accurest.dsl.internal.QueryParameter +import io.codearte.accurest.dsl.internal.QueryParameters import io.codearte.accurest.dsl.internal.Request import java.util.regex.Pattern @@ -27,12 +30,47 @@ class WiremockRequestStubStrategy extends BaseWiremockStubStrategy { private Map buildRequestContent(ClientRequest request) { return ([method : request?.method?.clientValue, headers : buildClientRequestHeadersSection(request.headers) - ] << appendUrl(request) << appendBody(request)).findAll { it.value } + ] << appendUrl(request) << appendQueryParameters(request) << appendBody(request)).findAll { it.value } } private Map appendUrl(ClientRequest clientRequest) { - Object url = clientRequest?.url?.clientValue - return url instanceof Pattern ? [urlPattern: ((Pattern)url).pattern()] : [url: url] + def urlPath = clientRequest?.urlPath?.clientValue + if (urlPath) { + return [urlPath: urlPath] + } + def url = clientRequest?.url?.clientValue + return url instanceof Pattern ? [urlPattern: url.pattern()] : [url: url] + } + + private Map appendQueryParameters(ClientRequest clientRequest) { + def queryParameters = clientRequest?.urlPath?.queryParameters ?: clientRequest?.url?.queryParameters + return queryParameters && !queryParameters.parameters.isEmpty() ? + [queryParameters: buildUrlPathQueryParameters(queryParameters)] : [:] + } + + private Map buildUrlPathQueryParameters(QueryParameters queryParameters) { + return queryParameters.parameters.collectEntries { QueryParameter param -> + parseQueryParameter(param.name, param.clientValue) + } + } + + protected Map parseQueryParameter(String name, MatchingStrategy matchingStrategy) { + return buildQueryParameter(name, matchingStrategy.clientValue, matchingStrategy.type) + } + + protected Map parseQueryParameter(String name, Object value) { + return buildQueryParameter(name, value, MatchingStrategy.Type.EQUAL_TO) + } + + protected Map parseQueryParameter(String name, Pattern pattern) { + return buildQueryParameter(name, pattern.pattern(), MatchingStrategy.Type.MATCHING) + } + + private Map buildQueryParameter(String name, Object value, MatchingStrategy.Type type) { + if (value instanceof Pattern) { + value = value.pattern() + } + return [(name): [(type.name) : value]] } private Map appendBody(ClientRequest clientRequest) { diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/MatchingStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/MatchingStrategy.groovy new file mode 100644 index 0000000000..2be3567482 --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/MatchingStrategy.groovy @@ -0,0 +1,34 @@ +package io.codearte.accurest.dsl.internal + +import groovy.transform.CompileStatic +import groovy.transform.EqualsAndHashCode +import groovy.transform.ToString; + +@EqualsAndHashCode(includeFields = true) +@ToString(includePackage = false, includeFields = true, ignoreNulls = true, includeNames = true) +@CompileStatic +public class MatchingStrategy extends DslProperty { + + Type type + + MatchingStrategy(Object value, Type type) { + super(value) + this.type = type + } + + MatchingStrategy(DslProperty value, Type type) { + super(value.clientValue, value.serverValue) + this.type = type + } + + enum Type { + EQUAL_TO("equalTo"), CONTAINS("contains"), MATCHING("matches"), NOT_MATCHING("doesNotMatch") + + final String name + + Type(name) { + this.name = name + } + } + +} \ No newline at end of file diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameter.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameter.groovy new file mode 100644 index 0000000000..2e5a606c29 --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameter.groovy @@ -0,0 +1,29 @@ +package io.codearte.accurest.dsl.internal; + +import groovy.transform.CompileStatic; +import groovy.transform.EqualsAndHashCode; +import groovy.transform.ToString; + +@EqualsAndHashCode(includeFields = true) +@ToString(includePackage = false, includeFields = true, ignoreNulls = true, includeNames = true) +@CompileStatic +public class QueryParameter extends DslProperty { + + String name + + QueryParameter(String name, DslProperty dslProperty) { + super(dslProperty.clientValue, dslProperty.serverValue) + this.name = name + } + + QueryParameter(String name, MatchingStrategy matchingStrategy) { + super(matchingStrategy) + this.name = name + } + + QueryParameter(String name, Object value) { + super(value) + this.name = name + } + +} diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameters.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameters.groovy new file mode 100644 index 0000000000..c233681652 --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameters.groovy @@ -0,0 +1,45 @@ +package io.codearte.accurest.dsl.internal + +import groovy.transform.EqualsAndHashCode +import groovy.transform.ToString +import groovy.transform.TypeChecked + +@EqualsAndHashCode(includeFields = true) +@ToString(includePackage = false, includeFields = true, ignoreNulls = true, includeNames = true) +@TypeChecked +public class QueryParameters { + + List parameters = [] + + public void parameter(Map singleParameter) { + Map.Entry first = singleParameter.entrySet().first() + parameters << new QueryParameter(first?.key, first?.value) + } + + public void parameter(String parameterName, Object parameterValue) { + parameters << new QueryParameter(parameterName, parameterValue) + } + + def equalTo(Object value) { + return new MatchingStrategy(value, MatchingStrategy.Type.EQUAL_TO) + } + + def containing(Object value) { + return new MatchingStrategy(value, MatchingStrategy.Type.CONTAINS) + } + + def matching(Object value) { + return new MatchingStrategy(value, MatchingStrategy.Type.MATCHING) + } + + def notMatching(Object value) { + return new MatchingStrategy(value, MatchingStrategy.Type.NOT_MATCHING) + } + + void collect(Closure closure) { + parameters?.each { + parameter -> closure(parameter) + } + } + +} diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Request.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Request.groovy index 50bd42910c..dab8f873f2 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Request.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Request.groovy @@ -11,6 +11,7 @@ class Request extends Common { DslProperty method Url url + UrlPath urlPath Headers headers Body body @@ -20,6 +21,7 @@ class Request extends Common { Request(Request request) { this.method = request.method this.url = request.url + this.urlPath = request.urlPath this.headers = request.headers this.body = request.body } @@ -32,7 +34,7 @@ class Request extends Common { this.method = toDslProperty(method) } - void url(String url) { + void url(Object url) { this.url = new Url(url) } @@ -40,6 +42,38 @@ class Request extends Common { this.url = new Url(url) } + void url(Object url, @DelegatesTo(UrlPath) Closure closure) { + this.url = new Url(url) + closure.delegate = this.url + closure() + } + + void url(DslProperty url, @DelegatesTo(UrlPath) Closure closure) { + this.url = new Url(url) + closure.delegate = this.url + closure() + } + + void urlPath(String path) { + this.urlPath = new UrlPath(path) + } + + void urlPath(DslProperty path) { + this.urlPath = new UrlPath(path) + } + + void urlPath(String path, @DelegatesTo(UrlPath) Closure closure) { + this.urlPath = new UrlPath(path) + closure.delegate = urlPath + closure() + } + + void urlPath(DslProperty path, @DelegatesTo(UrlPath) Closure closure) { + this.urlPath = new UrlPath(path) + closure.delegate = urlPath + closure() + } + void headers(@DelegatesTo(Headers) Closure closure) { this.headers = new Headers() closure.delegate = headers diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Url.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Url.groovy index da91b4a248..42a26eb26c 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Url.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Url.groovy @@ -1,18 +1,28 @@ package io.codearte.accurest.dsl.internal +import groovy.transform.CompileStatic import groovy.transform.EqualsAndHashCode import groovy.transform.ToString @ToString(includePackage = false, includeFields = true, includeNames = true) @EqualsAndHashCode(includeFields = true) +@CompileStatic class Url extends DslProperty { - Url(DslProperty bodyAsValue) { - super(bodyAsValue.clientValue, bodyAsValue.serverValue) + QueryParameters queryParameters + + Url(DslProperty prop) { + super(prop.clientValue, prop.serverValue) } - Url(String bodyAsValue) { - super(bodyAsValue) + Url(Object url) { + super(url) } - + + void queryParameters(@DelegatesTo(QueryParameters) Closure closure) { + this.queryParameters = new QueryParameters() + closure.delegate = queryParameters + closure() + } + } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/UrlPath.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/UrlPath.groovy new file mode 100644 index 0000000000..ee1d3eed98 --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/UrlPath.groovy @@ -0,0 +1,20 @@ +package io.codearte.accurest.dsl.internal + +import groovy.transform.CompileStatic; +import groovy.transform.EqualsAndHashCode +import groovy.transform.ToString; + +@ToString(includePackage = false, includeFields = true, includeNames = true) +@EqualsAndHashCode(includeFields = true) +@CompileStatic +class UrlPath extends Url { + + UrlPath(String path) { + super(path) + } + + UrlPath(DslProperty path) { + super(path) + } + +} diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy index f5b61625e3..ba6211223d 100755 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy @@ -1,4 +1,6 @@ package io.codearte.accurest.dsl + +import groovy.json.JsonBuilder import groovy.json.JsonSlurper class WiremockGroovyDslSpec extends WiremockSpec { @@ -313,6 +315,110 @@ class WiremockGroovyDslSpec extends WiremockSpec { ''') } + def "should generate request with urlPath and queryParameters for client side"() { + given: + GroovyDsl groovyDsl = GroovyDsl.make { + request { + method 'GET' + urlPath($(client("users"), server("items"))) { + queryParameters { + parameter 'limit': $(client(equalTo("20")), server(containing("10"))) + parameter 'offset': containing("10") + parameter 'filter': "email" + parameter 'sort': ~/^[0-9]{10}$/ + parameter 'search': $(client(notMatching(~/^\/[0-9]{2}$/)), server(containing("10"))) + parameter 'age': notMatching("^\\w*\$") + parameter 'name': matching("Denis.*") + } + } + } + response { + status 200 + } + } + when: + def json = toWiremockClientJsonStub(groovyDsl) + then: + parseJson(json) == parseJson(''' + { + "request": { + "method": "GET", + "urlPath":"users", + "queryParameters": { + "offset": { + "contains": "10" + }, + "limit": { + "equalTo": "20" + }, + "filter": { + "equalTo": "email" + }, + "sort": { + "matches": "^[0-9]{10}$" + }, + "search": { + "doesNotMatch": "^/[0-9]{2}$" + }, + "age": { + "doesNotMatch": "^\\\\w*$" + }, + "name": { + "matches": "Denis.*" + } + } + }, + "response": { + "status": 200, + } + } + ''') + and: + stubMappingIsValidWiremockStub(json) + } + + def "should generate request with url and queryParameters for client side"() { + given: + GroovyDsl groovyDsl = GroovyDsl.make { + request { + method 'GET' + url(regex(/users\/[0-9]*/)) { + queryParameters { + parameter 'age': notMatching("^\\w*\$") + parameter 'name': matching("Denis.*") + } + } + } + response { + status 200 + } + } + when: + def json = toWiremockClientJsonStub(groovyDsl) + then: + parseJson(json) == parseJson(''' + { + "request": { + "method": "GET", + "urlPattern": "users/[0-9]*", + "queryParameters": { + "age": { + "doesNotMatch": "^\\\\w*$" + }, + "name": { + "matches": "Denis.*" + } + } + }, + "response": { + "status": 200, + } + } + ''') + and: + stubMappingIsValidWiremockStub(json) + } + def "should generate stub with some headers section for client side"() { given: GroovyDsl groovyDsl = GroovyDsl.make { @@ -347,4 +453,16 @@ class WiremockGroovyDslSpec extends WiremockSpec { } ''') } + + def toJsonString(value) { + new JsonBuilder(value).toPrettyString() + } + + def parseJson(json) { + new JsonSlurper().parseText(json) + } + + def toWiremockClientJsonStub(groovyDsl) { + new WiremockStubStrategy(groovyDsl).toWiremockClientStub() + } } From 1eb035189e3915e1ba9c84be58e3213584884a62 Mon Sep 17 00:00:00 2001 From: Denis Stepanov Date: Fri, 5 Jun 2015 12:56:58 +0200 Subject: [PATCH 018/119] Add more tests --- .../accurest/dsl/WiremockGroovyDslSpec.groovy | 60 ++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy index ba6211223d..6e3843e146 100755 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy @@ -343,7 +343,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { { "request": { "method": "GET", - "urlPath":"users", + "urlPath": "users", "queryParameters": { "offset": { "contains": "10" @@ -377,6 +377,64 @@ class WiremockGroovyDslSpec extends WiremockSpec { stubMappingIsValidWiremockStub(json) } + def "should generate request with urlPath for client side"() { + given: + GroovyDsl groovyDsl = GroovyDsl.make { + request { + method 'GET' + urlPath $(client("boxes"), server("items")) + } + response { + status 200 + } + } + when: + def json = toWiremockClientJsonStub(groovyDsl) + then: + parseJson(json) == parseJson(''' + { + "request": { + "method": "GET", + "urlPath": "boxes" + }, + "response": { + "status": 200, + } + } + ''') + and: + stubMappingIsValidWiremockStub(json) + } + + def "should generate simple request with urlPath for client side"() { + given: + GroovyDsl groovyDsl = GroovyDsl.make { + request { + method 'GET' + urlPath "boxes" + } + response { + status 200 + } + } + when: + def json = toWiremockClientJsonStub(groovyDsl) + then: + parseJson(json) == parseJson(''' + { + "request": { + "method": "GET", + "urlPath": "boxes" + }, + "response": { + "status": 200, + } + } + ''') + and: + stubMappingIsValidWiremockStub(json) + } + def "should generate request with url and queryParameters for client side"() { given: GroovyDsl groovyDsl = GroovyDsl.make { From cd226a009dec20f7d630ce4e4307bb385c50a29b Mon Sep 17 00:00:00 2001 From: Denis Stepanov Date: Mon, 8 Jun 2015 13:29:21 +0200 Subject: [PATCH 019/119] PR code review comments --- .../dsl/WiremockRequestStubStrategy.groovy | 23 ++++++++++--------- .../dsl/internal/MatchingStrategy.groovy | 2 +- .../dsl/internal/QueryParameter.groovy | 2 +- .../dsl/internal/QueryParameters.groovy | 20 ++++++---------- .../accurest/dsl/WiremockGroovyDslSpec.groovy | 6 ++--- 5 files changed, 24 insertions(+), 29 deletions(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy index aa0b0f6bbc..298cc1b2ec 100755 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy @@ -34,42 +34,43 @@ class WiremockRequestStubStrategy extends BaseWiremockStubStrategy { } private Map appendUrl(ClientRequest clientRequest) { - def urlPath = clientRequest?.urlPath?.clientValue + Object urlPath = clientRequest?.urlPath?.clientValue if (urlPath) { return [urlPath: urlPath] } - def url = clientRequest?.url?.clientValue + Object url = clientRequest?.url?.clientValue return url instanceof Pattern ? [urlPattern: url.pattern()] : [url: url] } private Map appendQueryParameters(ClientRequest clientRequest) { - def queryParameters = clientRequest?.urlPath?.queryParameters ?: clientRequest?.url?.queryParameters + QueryParameters queryParameters = clientRequest?.urlPath?.queryParameters ?: clientRequest?.url?.queryParameters return queryParameters && !queryParameters.parameters.isEmpty() ? [queryParameters: buildUrlPathQueryParameters(queryParameters)] : [:] } - private Map buildUrlPathQueryParameters(QueryParameters queryParameters) { + private Map buildUrlPathQueryParameters(QueryParameters queryParameters) { return queryParameters.parameters.collectEntries { QueryParameter param -> parseQueryParameter(param.name, param.clientValue) } } - protected Map parseQueryParameter(String name, MatchingStrategy matchingStrategy) { + protected Map parseQueryParameter(String name, MatchingStrategy matchingStrategy) { return buildQueryParameter(name, matchingStrategy.clientValue, matchingStrategy.type) } - protected Map parseQueryParameter(String name, Object value) { + protected Map parseQueryParameter(String name, Object value) { return buildQueryParameter(name, value, MatchingStrategy.Type.EQUAL_TO) } - protected Map parseQueryParameter(String name, Pattern pattern) { + protected Map parseQueryParameter(String name, Pattern pattern) { return buildQueryParameter(name, pattern.pattern(), MatchingStrategy.Type.MATCHING) } - private Map buildQueryParameter(String name, Object value, MatchingStrategy.Type type) { - if (value instanceof Pattern) { - value = value.pattern() - } + private Map buildQueryParameter(String name, Pattern pattern, MatchingStrategy.Type type) { + return buildQueryParameter(name, pattern.pattern(), type) + } + + private Map buildQueryParameter(String name, Object value, MatchingStrategy.Type type) { return [(name): [(type.name) : value]] } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/MatchingStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/MatchingStrategy.groovy index 2be3567482..c385bb1939 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/MatchingStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/MatchingStrategy.groovy @@ -7,7 +7,7 @@ import groovy.transform.ToString; @EqualsAndHashCode(includeFields = true) @ToString(includePackage = false, includeFields = true, ignoreNulls = true, includeNames = true) @CompileStatic -public class MatchingStrategy extends DslProperty { +class MatchingStrategy extends DslProperty { Type type diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameter.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameter.groovy index 2e5a606c29..78d8e34009 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameter.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameter.groovy @@ -7,7 +7,7 @@ import groovy.transform.ToString; @EqualsAndHashCode(includeFields = true) @ToString(includePackage = false, includeFields = true, ignoreNulls = true, includeNames = true) @CompileStatic -public class QueryParameter extends DslProperty { +class QueryParameter extends DslProperty { String name diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameters.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameters.groovy index c233681652..eedaf5654b 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameters.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameters.groovy @@ -7,39 +7,33 @@ import groovy.transform.TypeChecked @EqualsAndHashCode(includeFields = true) @ToString(includePackage = false, includeFields = true, ignoreNulls = true, includeNames = true) @TypeChecked -public class QueryParameters { +class QueryParameters { List parameters = [] - public void parameter(Map singleParameter) { + void parameter(Map singleParameter) { Map.Entry first = singleParameter.entrySet().first() parameters << new QueryParameter(first?.key, first?.value) } - public void parameter(String parameterName, Object parameterValue) { + void parameter(String parameterName, Object parameterValue) { parameters << new QueryParameter(parameterName, parameterValue) } - def equalTo(Object value) { + MatchingStrategy equalTo(Object value) { return new MatchingStrategy(value, MatchingStrategy.Type.EQUAL_TO) } - def containing(Object value) { + MatchingStrategy containing(Object value) { return new MatchingStrategy(value, MatchingStrategy.Type.CONTAINS) } - def matching(Object value) { + MatchingStrategy matching(Object value) { return new MatchingStrategy(value, MatchingStrategy.Type.MATCHING) } - def notMatching(Object value) { + MatchingStrategy notMatching(Object value) { return new MatchingStrategy(value, MatchingStrategy.Type.NOT_MATCHING) } - void collect(Closure closure) { - parameters?.each { - parameter -> closure(parameter) - } - } - } diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy index 6e3843e146..de857e0efc 100755 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy @@ -512,15 +512,15 @@ class WiremockGroovyDslSpec extends WiremockSpec { ''') } - def toJsonString(value) { + String toJsonString(value) { new JsonBuilder(value).toPrettyString() } - def parseJson(json) { + Object parseJson(json) { new JsonSlurper().parseText(json) } - def toWiremockClientJsonStub(groovyDsl) { + String toWiremockClientJsonStub(groovyDsl) { new WiremockStubStrategy(groovyDsl).toWiremockClientStub() } } From 2420ea7ad713fe3c7f92b85a4503888352824fb6 Mon Sep 17 00:00:00 2001 From: Denis Stepanov Date: Tue, 9 Jun 2015 13:20:00 +0200 Subject: [PATCH 020/119] Spock generation for url path --- .../builder/SpockMethodBodyBuilder.groovy | 116 ++++++++++++------ .../builder/SpockMethodBuilderSpec.groovy | 38 ++++++ 2 files changed, 116 insertions(+), 38 deletions(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy index 532e429952..41734b0a3a 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy @@ -5,6 +5,11 @@ import groovy.transform.PackageScope import io.codearte.accurest.dsl.GroovyDsl import io.codearte.accurest.dsl.internal.ExecutionProperty import io.codearte.accurest.dsl.internal.Header +import io.codearte.accurest.dsl.internal.MatchingStrategy +import io.codearte.accurest.dsl.internal.QueryParameter +import io.codearte.accurest.dsl.internal.Request +import io.codearte.accurest.dsl.internal.Response +import io.codearte.accurest.dsl.internal.UrlPath import java.util.regex.Pattern @@ -20,46 +25,81 @@ class SpockMethodBodyBuilder { } void appendTo(BlockBuilder blockBuilder) { - blockBuilder.startBlock() - blockBuilder.addLine('given:').startBlock() - blockBuilder.addLine('def request = given()') - blockBuilder.indent() - stubDefinition.request.headers?.collect { Header header -> - blockBuilder.addLine(".header('${header.name}', '${header.serverValue}')") - } - if (stubDefinition.request.body) { - String matches = new JsonOutput().toJson(stubDefinition.request.body.serverValue) - blockBuilder.addLine(".body('$matches')") - } - - blockBuilder.unindent().endBlock().addEmptyLine() - - blockBuilder.addLine('when:').startBlock() - blockBuilder.addLine('def response = given().spec(request)') - blockBuilder.indent() - blockBuilder.addLine(".${stubDefinition.request.method.serverValue.toLowerCase()}(\"$stubDefinition.request.url.serverValue\")") - blockBuilder.unindent().endBlock().addEmptyLine() - - blockBuilder.addLine('then:').startBlock() - blockBuilder.addLine("response.statusCode == $stubDefinition.response.status.serverValue") - - stubDefinition.response.headers?.collect { Header header -> - blockBuilder.addLine("response.header('$header.name') == '$header.serverValue'") - } - if (stubDefinition.response.body) { - blockBuilder.endBlock() - blockBuilder.addLine('and:').startBlock() - blockBuilder.addLine('def responseBody = new JsonSlurper().parseText(response.body.asString())') - def responseBody = stubDefinition.response.body.serverValue - if (responseBody instanceof List) { - processArrayElements(responseBody, "", blockBuilder) - } else { - processMapElement(responseBody, blockBuilder, "") + Request request = stubDefinition.request + Response response = stubDefinition.response + blockBuilder.with { + startBlock() + addLine('given:').startBlock() + addLine('def request = given()') + indent() + request.headers?.collect { Header header -> + addLine(".header('${header.name}', '${header.serverValue}')") + } + if (request.body) { + String matches = new JsonOutput().toJson(request.body.serverValue) + addLine(".body('$matches')") } - } - blockBuilder.endBlock() - blockBuilder.endBlock() + unindent().endBlock().addEmptyLine() + + addLine('when:').startBlock() + addLine('def response = given().spec(request)') + indent() + + String url = buildUrl(request) + String method = request.method.serverValue.toLowerCase() + + blockBuilder.addLine(/.${method}("$url")/) + unindent().endBlock().addEmptyLine() + + addLine('then:').startBlock() + addLine("response.statusCode == $response.status.serverValue") + + response.headers?.collect { Header header -> + addLine("response.header('$header.name') == '$header.serverValue'") + } + if (response.body) { + endBlock() + addLine('and:').startBlock() + addLine('def responseBody = new JsonSlurper().parseText(response.body.asString())') + def responseBody = response.body.serverValue + if (responseBody instanceof List) { + processArrayElements(responseBody, "", blockBuilder) + } else { + processMapElement(responseBody, blockBuilder, "") + } + } + endBlock() + + endBlock() + } + } + + private String buildUrl(Request request) { + if (request.url) + return request.url.serverValue; + if (request.urlPath) + return buildUrlFromUrlPath(request.urlPath) + throw new IllegalStateException("URL is not set!") + } + + private String buildUrlFromUrlPath(UrlPath urlPath) { + String params = urlPath.queryParameters.parameters.inject([]) { result, param -> + result << "${param.name}=${URLEncoder.encode(resolveParamValue(param).toString(), "UTF8")}" + }.join('&') + return "$urlPath.serverValue?$params" + } + + private String resolveParamValue(QueryParameter param) { + resolveParamValue(param.serverValue) + } + + private String resolveParamValue(Object value) { + value.toString() + } + + private String resolveParamValue(MatchingStrategy matchingStrategy) { + matchingStrategy.serverValue.toString() } private void processBodyElement(BlockBuilder blockBuilder, String rootProperty, def element) { diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy index 0bde0a9ea3..ce1d00fa71 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy @@ -169,4 +169,42 @@ class SpockMethodBuilderSpec extends Specification { blockBuilder.toString().contains("responseBody.property2 ==~ java.util.regex.Pattern.compile('[0-9]{3}')") } + def "should generate a call with an url path and query parameters"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method 'GET' + urlPath('/users') { + queryParameters { + parameter 'limit': $(client(equalTo("20")), server(equalTo("10"))) + parameter 'offset': $(client(containing("20")), server(equalTo("20"))) + parameter 'filter': "email" + parameter 'sort': equalTo("name") + parameter 'search': $(client(notMatching(~/^\/[0-9]{2}$/)), server("55")) + parameter 'age': $(client(notMatching("^\\w*\$")), server("99")) + parameter 'name': $(client(matching("Denis.*")), server("Denis.Stepanov")) + } + } + } + response { + status 200 + body """ + { + "property1": "a", + "property2": "b" + } + """ + } + } + SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + def spockTest = blockBuilder.toString() + then: + spockTest.contains('get("/users?limit=10&offset=20&filter=email&sort=name&search=55&age=99&name=Denis.Stepanov")') + spockTest.contains('responseBody.property1 == "a"') + spockTest.contains('responseBody.property2 == "b"') + } + } From c0580dfe7a8db3facb73a71474575dd6e638075c Mon Sep 17 00:00:00 2001 From: Denis Stepanov Date: Tue, 9 Jun 2015 17:40:04 +0200 Subject: [PATCH 021/119] Add server values validation --- .../dsl/internal/QueryParameter.groovy | 7 +- .../codearte/accurest/dsl/internal/Url.groovy | 4 + .../accurest/util/ValidateUtils.groovy | 39 +++++++++ .../accurest/dsl/WiremockGroovyDslSpec.groovy | 80 ++++++++++++++++--- 4 files changed, 120 insertions(+), 10 deletions(-) create mode 100644 accurest-core/src/main/groovy/io/codearte/accurest/util/ValidateUtils.groovy diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameter.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameter.groovy index 78d8e34009..042da081f2 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameter.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameter.groovy @@ -2,7 +2,9 @@ package io.codearte.accurest.dsl.internal; import groovy.transform.CompileStatic; import groovy.transform.EqualsAndHashCode; -import groovy.transform.ToString; +import groovy.transform.ToString + +import static io.codearte.accurest.util.ValidateUtils.validateServerValueIsAvailable; @EqualsAndHashCode(includeFields = true) @ToString(includePackage = false, includeFields = true, ignoreNulls = true, includeNames = true) @@ -13,16 +15,19 @@ class QueryParameter extends DslProperty { QueryParameter(String name, DslProperty dslProperty) { super(dslProperty.clientValue, dslProperty.serverValue) + validateServerValueIsAvailable(dslProperty.serverValue, "Query parameter '$name'") this.name = name } QueryParameter(String name, MatchingStrategy matchingStrategy) { super(matchingStrategy) + validateServerValueIsAvailable(matchingStrategy, "Query parameter '$name'") this.name = name } QueryParameter(String name, Object value) { super(value) + validateServerValueIsAvailable(value, "Query parameter '$name'") this.name = name } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Url.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Url.groovy index 42a26eb26c..09eae6d094 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Url.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Url.groovy @@ -4,6 +4,8 @@ import groovy.transform.CompileStatic import groovy.transform.EqualsAndHashCode import groovy.transform.ToString +import static io.codearte.accurest.util.ValidateUtils.validateServerValueIsAvailable + @ToString(includePackage = false, includeFields = true, includeNames = true) @EqualsAndHashCode(includeFields = true) @CompileStatic @@ -13,10 +15,12 @@ class Url extends DslProperty { Url(DslProperty prop) { super(prop.clientValue, prop.serverValue) + validateServerValueIsAvailable(prop.serverValue, "Url") } Url(Object url) { super(url) + validateServerValueIsAvailable(url, "Url") } void queryParameters(@DelegatesTo(QueryParameters) Closure closure) { diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/util/ValidateUtils.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/ValidateUtils.groovy new file mode 100644 index 0000000000..9c07eaa3e4 --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/ValidateUtils.groovy @@ -0,0 +1,39 @@ +package io.codearte.accurest.util + +import io.codearte.accurest.dsl.internal.DslProperty +import io.codearte.accurest.dsl.internal.MatchingStrategy + +import java.util.regex.Pattern + +class ValidateUtils { + + static Object validateServerValueIsAvailable(Object value) { + validateServerValueIsAvailable(value, "Server value") + return value + } + + static Object validateServerValueIsAvailable(Object value, String msg) { + validateServerValue(value, msg) + return value + } + + static void validateServerValue(Pattern pattern, String msg) { + throw new IllegalStateException("$msg can't be a pattern") + } + + static void validateServerValue(MatchingStrategy matchingStrategy, String msg) { + if (matchingStrategy.type != MatchingStrategy.Type.EQUAL_TO) { + throw new IllegalStateException("$msg can't be of matching type: $matchingStrategy.type") + } + validateServerValue(matchingStrategy.serverValue, msg) + } + + static void validateServerValue(DslProperty value, String msg) { + validateServerValue(value.serverValue, msg) + } + + static void validateServerValue(Object value, String msg) { + // OK + } + +} diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy index de857e0efc..e5e33d9e04 100755 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy @@ -322,13 +322,13 @@ class WiremockGroovyDslSpec extends WiremockSpec { method 'GET' urlPath($(client("users"), server("items"))) { queryParameters { - parameter 'limit': $(client(equalTo("20")), server(containing("10"))) - parameter 'offset': containing("10") + parameter 'limit': $(client(equalTo("20")), server("10")) + parameter 'offset': $(client(containing("10")), server("10")) parameter 'filter': "email" - parameter 'sort': ~/^[0-9]{10}$/ - parameter 'search': $(client(notMatching(~/^\/[0-9]{2}$/)), server(containing("10"))) - parameter 'age': notMatching("^\\w*\$") - parameter 'name': matching("Denis.*") + parameter 'sort': $(client(~/^[0-9]{10}$/), server("1234567890")) + parameter 'search': $(client(notMatching(~/^\/[0-9]{2}$/)), server("10")) + parameter 'age': $(client(notMatching("^\\w*\$")), server(10)) + parameter 'name': $(client(matching("Denis.*")), server("Denis")) } } } @@ -435,9 +435,9 @@ class WiremockGroovyDslSpec extends WiremockSpec { stubMappingIsValidWiremockStub(json) } - def "should generate request with url and queryParameters for client side"() { - given: - GroovyDsl groovyDsl = GroovyDsl.make { + def "should not allow regexp in url for server value"() { + when: + GroovyDsl.make { request { method 'GET' url(regex(/users\/[0-9]*/)) { @@ -451,6 +451,68 @@ class WiremockGroovyDslSpec extends WiremockSpec { status 200 } } + then: + def e = thrown(IllegalStateException) + e.message.contains "Url can't be a pattern" + } + + def "should not allow regexp in query parameter for server value"() { + when: + GroovyDsl.make { + request { + method 'GET' + url("abc") { + queryParameters { + parameter 'age': $(client(notMatching("^\\w*\$")), server(regex(".*"))) + } + } + } + response { + status 200 + } + } + then: + def e = thrown(IllegalStateException) + e.message.contains "Query parameter 'age' can't be a pattern" + } + + def "should not allow query parameter unresolvable for a server value"() { + when: + GroovyDsl.make { + request { + method 'GET' + urlPath("users") { + queryParameters { + parameter 'age': notMatching("^\\w*\$") + parameter 'name': matching("Denis.*") + } + } + } + response { + status 200 + } + } + then: + def e = thrown(IllegalStateException) + e.message.contains "Query parameter 'age' can't be of matching type: NOT_MATCHING" + } + + def "should generate request with url and queryParameters for client side"() { + given: + GroovyDsl groovyDsl = GroovyDsl.make { + request { + method 'GET' + url($(client(regex(/users\/[0-9]*/)), server("users/123"))) { + queryParameters { + parameter 'age': $(client(notMatching("^\\w*\$")), server(10)) + parameter 'name': $(client(matching("Denis.*")), server("Denis")) + } + } + } + response { + status 200 + } + } when: def json = toWiremockClientJsonStub(groovyDsl) then: From 86efa5f242c10c6c24eaa91f6309ea15d123af05 Mon Sep 17 00:00:00 2001 From: Denis Stepanov Date: Wed, 10 Jun 2015 10:53:11 +0200 Subject: [PATCH 022/119] Code review changes --- .../accurest/util/ValidateUtils.groovy | 6 +- .../builder/SpockMethodBuilderSpec.groovy | 56 +++++++++---------- .../accurest/dsl/WiremockGroovyDslSpec.groovy | 6 +- 3 files changed, 35 insertions(+), 33 deletions(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/util/ValidateUtils.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/ValidateUtils.groovy index 9c07eaa3e4..eac7bce25c 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/util/ValidateUtils.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/ValidateUtils.groovy @@ -1,10 +1,12 @@ package io.codearte.accurest.util +import groovy.transform.TypeChecked import io.codearte.accurest.dsl.internal.DslProperty import io.codearte.accurest.dsl.internal.MatchingStrategy import java.util.regex.Pattern +@TypeChecked class ValidateUtils { static Object validateServerValueIsAvailable(Object value) { @@ -18,12 +20,12 @@ class ValidateUtils { } static void validateServerValue(Pattern pattern, String msg) { - throw new IllegalStateException("$msg can't be a pattern") + throw new IllegalStateException("$msg can't be a pattern for the server side") } static void validateServerValue(MatchingStrategy matchingStrategy, String msg) { if (matchingStrategy.type != MatchingStrategy.Type.EQUAL_TO) { - throw new IllegalStateException("$msg can't be of matching type: $matchingStrategy.type") + throw new IllegalStateException("$msg can't be of a matching type: $matchingStrategy.type for the server side") } validateServerValue(matchingStrategy.serverValue, msg) } diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy index ce1d00fa71..0aff49357f 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy @@ -171,40 +171,40 @@ class SpockMethodBuilderSpec extends Specification { def "should generate a call with an url path and query parameters"() { given: - GroovyDsl contractDsl = GroovyDsl.make { - request { - method 'GET' - urlPath('/users') { - queryParameters { - parameter 'limit': $(client(equalTo("20")), server(equalTo("10"))) - parameter 'offset': $(client(containing("20")), server(equalTo("20"))) - parameter 'filter': "email" - parameter 'sort': equalTo("name") - parameter 'search': $(client(notMatching(~/^\/[0-9]{2}$/)), server("55")) - parameter 'age': $(client(notMatching("^\\w*\$")), server("99")) - parameter 'name': $(client(matching("Denis.*")), server("Denis.Stepanov")) + GroovyDsl contractDsl = GroovyDsl.make { + request { + method 'GET' + urlPath('/users') { + queryParameters { + parameter 'limit': $(client(equalTo("20")), server(equalTo("10"))) + parameter 'offset': $(client(containing("20")), server(equalTo("20"))) + parameter 'filter': "email" + parameter 'sort': equalTo("name") + parameter 'search': $(client(notMatching(~/^\/[0-9]{2}$/)), server("55")) + parameter 'age': $(client(notMatching("^\\w*\$")), server("99")) + parameter 'name': $(client(matching("Denis.*")), server("Denis.Stepanov")) + } } } - } - response { - status 200 - body """ - { - "property1": "a", - "property2": "b" + response { + status 200 + body """ + { + "property1": "a", + "property2": "b" + } + """ } - """ } - } - SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) - BlockBuilder blockBuilder = new BlockBuilder(" ") + SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") when: - builder.appendTo(blockBuilder) - def spockTest = blockBuilder.toString() + builder.appendTo(blockBuilder) + def spockTest = blockBuilder.toString() then: - spockTest.contains('get("/users?limit=10&offset=20&filter=email&sort=name&search=55&age=99&name=Denis.Stepanov")') - spockTest.contains('responseBody.property1 == "a"') - spockTest.contains('responseBody.property2 == "b"') + spockTest.contains('get("/users?limit=10&offset=20&filter=email&sort=name&search=55&age=99&name=Denis.Stepanov")') + spockTest.contains('responseBody.property1 == "a"') + spockTest.contains('responseBody.property2 == "b"') } } diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy index e5e33d9e04..7d8e5f1d49 100755 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy @@ -453,7 +453,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { } then: def e = thrown(IllegalStateException) - e.message.contains "Url can't be a pattern" + e.message.contains "Url can't be a pattern for the server side" } def "should not allow regexp in query parameter for server value"() { @@ -473,7 +473,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { } then: def e = thrown(IllegalStateException) - e.message.contains "Query parameter 'age' can't be a pattern" + e.message.contains "Query parameter 'age' can't be a pattern for the server side" } def "should not allow query parameter unresolvable for a server value"() { @@ -494,7 +494,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { } then: def e = thrown(IllegalStateException) - e.message.contains "Query parameter 'age' can't be of matching type: NOT_MATCHING" + e.message.contains "Query parameter 'age' can't be of a matching type: NOT_MATCHING for the server side" } def "should generate request with url and queryParameters for client side"() { From d45eb5ccc475dcf8a4328c6033328f3cebb5a18d Mon Sep 17 00:00:00 2001 From: Jakub Kubrynski Date: Wed, 10 Jun 2015 22:44:38 +0200 Subject: [PATCH 023/119] Release version: 0.6.4 [ci skip] From 58b47fd6b4da6641503e34fac41840f22084ad89 Mon Sep 17 00:00:00 2001 From: Denis Stepanov Date: Thu, 11 Jun 2015 13:13:15 +0200 Subject: [PATCH 024/119] Implement selecting compare method by content type --- .../dsl/WiremockRequestStubStrategy.groovy | 15 +++- .../accurest/dsl/internal/Body.groovy | 51 +++++++++-- .../accurest/dsl/WiremockGroovyDslSpec.groovy | 90 +++++++++++++++++++ 3 files changed, 149 insertions(+), 7 deletions(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy index 298cc1b2ec..3390b6456b 100755 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy @@ -2,6 +2,7 @@ package io.codearte.accurest.dsl import groovy.transform.PackageScope import groovy.transform.TypeChecked import io.codearte.accurest.dsl.internal.ClientRequest +import io.codearte.accurest.dsl.internal.Header import io.codearte.accurest.dsl.internal.MatchingStrategy import io.codearte.accurest.dsl.internal.QueryParameter import io.codearte.accurest.dsl.internal.QueryParameters @@ -95,7 +96,19 @@ class WiremockRequestStubStrategy extends BaseWiremockStubStrategy { } ))]]] } - return [bodyPatterns: [[equalTo: parseBody(body)]]] + + return [bodyPatterns: [[(getCompareType()): parseBody(body)]]] + } + + private String getCompareType() { + Header contentType = request.headers?.entries.find { it.name == "Content-Type" } + if (contentType && contentType.clientValue.toString().endsWith("json")) { + return "equalToJson" + } + if (contentType && contentType.clientValue.toString().endsWith("xml")) { + return "equalToXml" + } + return "equalTo" } protected String parseBody(Object body) { diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Body.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Body.groovy index 315135d939..c68380f0f2 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Body.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Body.groovy @@ -1,5 +1,6 @@ package io.codearte.accurest.dsl.internal +import groovy.json.JsonException import groovy.json.JsonSlurper import groovy.transform.EqualsAndHashCode import groovy.transform.ToString @@ -9,6 +10,8 @@ import org.codehaus.groovy.runtime.GStringImpl import java.util.regex.Matcher import java.util.regex.Pattern +import static org.apache.commons.lang3.StringEscapeUtils.escapeXml11 + @ToString(includePackage = false, includeFields = true, includeNames = true) @EqualsAndHashCode(includeFields = true) class Body extends DslProperty { @@ -55,15 +58,51 @@ class Body extends DslProperty { * @return JSON structure with replaced client / server side parts */ private static Object extractValue(GString bodyAsValue, Closure valueProvider) { - GString gString = new GStringImpl(bodyAsValue.values.clone(), bodyAsValue.strings.clone()) - Object[] values = bodyAsValue.values.collect { it instanceof DslProperty ? valueProvider(it) : it } as Object[] - Object[] valuesWithRegexpsAsTransformedStrings = values.collect { - it instanceof Pattern ? String.format(JSON_VALUE_PATTERN_FOR_REGEX, it.toString()) : it - } as Object[] - def parsedJson = new JsonSlurper().parseText(new GStringImpl(valuesWithRegexpsAsTransformedStrings, gString.strings)) + try { + return extractValueForJSON(bodyAsValue, valueProvider) + } catch(JsonException e) { + // Not a JSON format + return extractValueForXML(bodyAsValue, valueProvider) + } + return bodyAsValue + } + + private static Object extractValueForJSON(GString bodyAsValue, Closure valueProvider) { + GString transformedString = new GStringImpl( + bodyAsValue.values.collect { transformJSONStringValue(it, valueProvider) } as Object[], + bodyAsValue.strings.clone() + ) + def parsedJson = new JsonSlurper().parseText(transformedString) return convertAllTemporaryRegexPlaceholdersBackToPatterns(parsedJson) } + private static GStringImpl extractValueForXML(GString bodyAsValue, Closure valueProvider) { + return new GStringImpl( + bodyAsValue.values.collect { transformXMLStringValue(it, valueProvider) } as Object[], + bodyAsValue.strings.clone() + ) + } + + private static String transformJSONStringValue(Object obj, Closure valueProvider) { + return obj.toString() + } + + private static String transformJSONStringValue(DslProperty dslProperty, Closure valueProvider) { + return transformJSONStringValue(valueProvider(dslProperty), valueProvider) + } + + private static String transformJSONStringValue(Pattern pattern, Closure valueProvider) { + return String.format(JSON_VALUE_PATTERN_FOR_REGEX, pattern.pattern()) + } + + private static String transformXMLStringValue(Object obj, Closure valueProvider) { + return escapeXml11(obj.toString()) + } + + private static String transformXMLStringValue(DslProperty dslProperty, Closure valueProvider) { + return transformXMLStringValue(valueProvider(dslProperty), valueProvider) + } + private static Object convertAllTemporaryRegexPlaceholdersBackToPatterns(parsedJson) { JsonConverter.transformValues(parsedJson, { Object value -> if (value instanceof String) { diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy index 7d8e5f1d49..4674441a09 100755 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy @@ -150,6 +150,96 @@ class WiremockGroovyDslSpec extends WiremockSpec { stubMappingIsValidWiremockStub(wiremockStub) } + def 'should use equalToJson when content type ends with json'() { + given: + GroovyDsl groovyDsl = GroovyDsl.make { + request { + method 'GET' + url "/users" + headers { + header "Content-Type", "customtype/json" + } + body """ + { + "name": "Jan" + } + """ + } + response { + status 200 + } + } + when: + String json = toWiremockClientJsonStub(groovyDsl) + then: + parseJson(json) == parseJson(''' + { + "request": { + "method": "GET", + "url": "/users", + "headers": { + "Content-Type": { + "equalTo": "customtype/json" + } + }, + "bodyPatterns": [ + { + "equalToJson":"{\\"name\\":\\"Jan\\"}" + } + ] + }, + "response": { + "status": 200 + } + } + ''') + and: + stubMappingIsValidWiremockStub(json) + } + + def 'should use equalToXml when content type ends with xml'() { + given: + GroovyDsl groovyDsl = GroovyDsl.make { + request { + method 'GET' + url "/users" + headers { + header "Content-Type", "customtype/xml" + } + body """${value(client('Jozo'), server('Denis'))}${value(client(""), server('1234567890'))}""" + } + response { + status 200 + } + } + when: + String json = toWiremockClientJsonStub(groovyDsl) + then: + parseJson(json) == parseJson(''' + { + "request": { + "method": "GET", + "url": "/users", + "headers": { + "Content-Type": { + "equalTo": "customtype/xml" + } + }, + "bodyPatterns": [ + { + "equalToXml":"Jozo<test>" + } + ] + }, + "response": { + "status": 200 + } + } + ''') + and: + stubMappingIsValidWiremockStub(json) + } + def 'should convert groovy dsl stub with regexp Body as String to wiremock stub for the client side'() { given: GroovyDsl groovyDsl = GroovyDsl.make { From a5502b35e27dffdf814b3e364a57023bc314ef75 Mon Sep 17 00:00:00 2001 From: Denis Stepanov Date: Thu, 11 Jun 2015 13:37:14 +0200 Subject: [PATCH 025/119] Implement selecting compare method by content type --- .../dsl/WiremockRequestStubStrategy.groovy | 55 ++++++++----------- .../accurest/dsl/internal/Body.groovy | 8 +++ 2 files changed, 32 insertions(+), 31 deletions(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy index 3390b6456b..31eb05300a 100755 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy @@ -2,7 +2,6 @@ package io.codearte.accurest.dsl import groovy.transform.PackageScope import groovy.transform.TypeChecked import io.codearte.accurest.dsl.internal.ClientRequest -import io.codearte.accurest.dsl.internal.Header import io.codearte.accurest.dsl.internal.MatchingStrategy import io.codearte.accurest.dsl.internal.QueryParameter import io.codearte.accurest.dsl.internal.QueryParameters @@ -80,32 +79,35 @@ class WiremockRequestStubStrategy extends BaseWiremockStubStrategy { if (body == null) { return [:] } - if (containsRegex(body)) { - return [bodyPatterns: [[matches: parseBody(convertJsonStructureToObjectUnderstandingStructure(body, - { it instanceof Pattern }, - { String json -> json.collect { - switch(it) { - case ('{'): return '\\{' - case ('}'): return '\\}' - default: return it - } - } .join('') - }, - { LinkedList list, String json -> - return json.replaceAll(TEMPORARY_PATTERN_HOLDER, { String a, String[] b -> list.pop() }) - } - ))]]] + if (clientRequest.body?.containsPattern) { + return [bodyPatterns: [[matches: escapeBodyForJson(body)]]] } - - return [bodyPatterns: [[(getCompareType()): parseBody(body)]]] + return [bodyPatterns: [[(getMatchType()): parseBody(body)]]] } - private String getCompareType() { - Header contentType = request.headers?.entries.find { it.name == "Content-Type" } - if (contentType && contentType.clientValue.toString().endsWith("json")) { + private Object escapeBodyForJson(Object body) { + return parseBody(convertJsonStructureToObjectUnderstandingStructure(body, + { it instanceof Pattern }, + { String json -> json.collect { + switch(it) { + case ('{'): return '\\{' + case ('}'): return '\\}' + default: return it + } + } .join('') + }, + { LinkedList list, String json -> + return json.replaceAll(TEMPORARY_PATTERN_HOLDER, { String a, String[] b -> list.pop() }) + } + )) + } + + private String getMatchType() { + String content = request.headers?.entries.find { it.name == "Content-Type" } ?.clientValue?.toString() + if (content?.endsWith("json")) { return "equalToJson" } - if (contentType && contentType.clientValue.toString().endsWith("xml")) { + if (content?.endsWith("xml")) { return "equalToXml" } return "equalTo" @@ -115,13 +117,4 @@ class WiremockRequestStubStrategy extends BaseWiremockStubStrategy { return body } - boolean containsRegex(Object bodyObject) { - String bodyString = bodyObject as String - return (bodyString =~ /\^.*\$/).find() - } - - boolean containsRegex(Map map) { - return map.values().any { it instanceof Pattern } - } - } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Body.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Body.groovy index c68380f0f2..78813a74a8 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Body.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Body.groovy @@ -19,6 +19,8 @@ class Body extends DslProperty { private static final Pattern TEMPORARY_PATTERN_HOLDER = Pattern.compile('REGEXP>>(.*)<<') private static final String JSON_VALUE_PATTERN_FOR_REGEX = 'REGEXP>>%s<<' + boolean containsPattern + Body(Map body) { super(extractValue(body, {it.clientValue}), extractValue(body, {it.serverValue})) } @@ -39,12 +41,18 @@ class Body extends DslProperty { Body(GString bodyAsValue) { super(extractValue(bodyAsValue, {it.clientValue}), extractValue(bodyAsValue, {it.serverValue})) + containsPattern = containsPattern(bodyAsValue) } Body(DslProperty bodyAsValue) { super(bodyAsValue.clientValue, bodyAsValue.serverValue) } + private boolean containsPattern(GString bodyAsValue) { + return bodyAsValue.values.collect { it instanceof DslProperty ? it.clientValue : it } + .find { it instanceof Pattern } + } + /** * Due to the fact that we allow users to have a body with GString and different values inside * we need to be prepared that they pass regexps around both on client and server side. From b5e1799468d40bf65b43f028726a7ab9990f0dbf Mon Sep 17 00:00:00 2001 From: "Jozef.Najman" Date: Thu, 11 Jun 2015 15:27:20 +0200 Subject: [PATCH 026/119] Fixed generating stubs from regex - one matcher for one regex --- .../dsl/WiremockRequestStubStrategy.groovy | 28 ++--- .../accurest/dsl/WiremockGroovyDslSpec.groovy | 113 ++++++++++++++---- .../codearte/accurest/dsl/WiremockSpec.groovy | 2 +- 3 files changed, 103 insertions(+), 40 deletions(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy index 31eb05300a..3c726f66d2 100755 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy @@ -80,26 +80,24 @@ class WiremockRequestStubStrategy extends BaseWiremockStubStrategy { return [:] } if (clientRequest.body?.containsPattern) { - return [bodyPatterns: [[matches: escapeBodyForJson(body)]]] + return [bodyPatterns: parseMatchesBody(body)] } return [bodyPatterns: [[(getMatchType()): parseBody(body)]]] } - private Object escapeBodyForJson(Object body) { - return parseBody(convertJsonStructureToObjectUnderstandingStructure(body, - { it instanceof Pattern }, - { String json -> json.collect { - switch(it) { - case ('{'): return '\\{' - case ('}'): return '\\}' - default: return it - } - } .join('') - }, - { LinkedList list, String json -> - return json.replaceAll(TEMPORARY_PATTERN_HOLDER, { String a, String[] b -> list.pop() }) + private Object parseMatchesBody(def responseBodyObject) { + def regexList = new ArrayList<>() + responseBodyObject.each { k, v -> + if (v instanceof List) { + v.each { + regexList.addAll(parseMatchesBody((Map)it)) } - )) + } else { + String regex = ".*${k}\":.?\"${v}\".*" + regexList.add([matches: regex]); + } + } + return regexList } private String getMatchType() { diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy index 4674441a09..452cc758ed 100755 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy @@ -161,7 +161,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { } body """ { - "name": "Jan" + "name": "${value(client('Jan'), server('Honza'))}" } """ } @@ -270,22 +270,20 @@ class WiremockGroovyDslSpec extends WiremockSpec { then: new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText(''' { - "request": { - "method": "GET", - "urlPattern": "/[0-9]{2}", - "bodyPatterns": [ - { - "matches":"\\\\{\\"personalId\\":\\"^[0-9]{11}$\\"\\\\}" - } - ] - }, - "response": { - "status": 200, - "body": "{\\"name\\":\\"Jan\\"}", - "headers": { - "Content-Type": "text/plain" - } - } + "request": { + "method": "GET", + "urlPattern": "/[0-9]{2}", + "bodyPatterns": [ + {"matches": ".*personalId\\":.?\\"^[0-9]{11}$\\".*"} + ] + }, + "response": { + "status": 200, + "body": "{\\"name\\":\\"Jan\\"}", + "headers": { + "Content-Type": "text/plain" + } + } } ''') and: @@ -336,9 +334,8 @@ class WiremockGroovyDslSpec extends WiremockSpec { }, "url": "/fraudcheck", "bodyPatterns": [ - { - "matches": "\\\\{\\"clientPesel\\":\\"[0-9]{10}\\",\\"loanAmount\\":123.123\\\\}" - } + {"matches": ".*clientPesel\\":.?\\"[0-9]{10}\\".*"}, + {"matches": ".*loanAmount\\":.?\\"123.123\\".*"} ] }, "response": { @@ -658,10 +655,78 @@ class WiremockGroovyDslSpec extends WiremockSpec { }, "X-Custom-Header": { "matches": "^.*2134.*$" - } - } - } - ''') + } + } + } + ''') + } + + def 'should convert groovy dsl stub with rich tree Body as String to wiremock stub for the client side'() { + given: + GroovyDsl groovyDsl = GroovyDsl.make { + request { + method('GET') + url $(client(~/\/[0-9]{2}/), server('/12')) + body """\ + { + "personalId": "${value(client(regex('[0-9]{11}')), server('57593728525'))}", + "firstName": "${value(client(regex('.*')), server('Bruce'))}", + "lastName": "${value(client(regex('.*')), server('Lee'))}", + "birthDate": "${value(client(regex('[0-9]{4}-[0-9]{2}-[0-9]{2}')), server('1985-12-12'))}", + "errors": [ + { + "propertyName": "${value(client(regex('[0-9]{2}')), server('04'))}", + "providerValue": "Test" + }, + { + "propertyName": "${value(client(regex('[0-9]{2}')), server('08'))}", + "providerValue": "Test" + } + ] + } + """ + } + response { + status 200 + body("""\ + { + "name": "Jan" + } + """ + ) + headers { + header 'Content-Type': 'text/plain' + } + } + } + when: + String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockClientStub() + then: + new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText(''' + { + "request": { + "method": "GET", + "urlPattern": "/[0-9]{2}", + "bodyPatterns": [ + {"matches": ".*birthDate\\":.?\\"[0-9]{4}-[0-9]{2}-[0-9]{2}\\".*"}, + {"matches": ".*propertyName\\":.?\\"[0-9]{2}\\".*"}, + {"matches": ".*providerValue\\":.?\\"Test\\".*"}, + {"matches": ".*propertyName\\":.?\\"[0-9]{2}\\".*"}, + {"matches": ".*providerValue\\":.?\\"Test\\".*"}, + {"matches": ".*firstName\\":.?\\".*\\".*"}, + {"matches": ".*lastName\\":.?\\".*\\".*"}, + {"matches": ".*personalId\\":.?\\"[0-9]{11}\\".*"} + ] + }, + "response": { + "status": 200, + "body": "{\\"name\\":\\"Jan\\"}", + "headers": { + "Content-Type": "text/plain" + } + } + } + ''') } String toJsonString(value) { diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockSpec.groovy index 796e603c11..ab1133c9ae 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockSpec.groovy @@ -5,7 +5,7 @@ import spock.lang.Specification import java.util.regex.Pattern -class WiremockSpec extends Specification { +abstract class WiremockSpec extends Specification { void stubMappingIsValidWiremockStub(String mappingDefinition) { StubMapping stubMapping = StubMapping.buildFrom(mappingDefinition) From 254a67cdb58b0285d6d41fa1156bacf422fe5aac Mon Sep 17 00:00:00 2001 From: "Jozef.Najman" Date: Thu, 11 Jun 2015 17:14:12 +0200 Subject: [PATCH 027/119] Reverted generating stubs from regex - one matcher for one regex --- .../dsl/WiremockRequestStubStrategy.groovy | 28 +++-- .../accurest/dsl/WiremockGroovyDslSpec.groovy | 113 ++++-------------- 2 files changed, 39 insertions(+), 102 deletions(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy index 3c726f66d2..31eb05300a 100755 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy @@ -80,24 +80,26 @@ class WiremockRequestStubStrategy extends BaseWiremockStubStrategy { return [:] } if (clientRequest.body?.containsPattern) { - return [bodyPatterns: parseMatchesBody(body)] + return [bodyPatterns: [[matches: escapeBodyForJson(body)]]] } return [bodyPatterns: [[(getMatchType()): parseBody(body)]]] } - private Object parseMatchesBody(def responseBodyObject) { - def regexList = new ArrayList<>() - responseBodyObject.each { k, v -> - if (v instanceof List) { - v.each { - regexList.addAll(parseMatchesBody((Map)it)) + private Object escapeBodyForJson(Object body) { + return parseBody(convertJsonStructureToObjectUnderstandingStructure(body, + { it instanceof Pattern }, + { String json -> json.collect { + switch(it) { + case ('{'): return '\\{' + case ('}'): return '\\}' + default: return it + } + } .join('') + }, + { LinkedList list, String json -> + return json.replaceAll(TEMPORARY_PATTERN_HOLDER, { String a, String[] b -> list.pop() }) } - } else { - String regex = ".*${k}\":.?\"${v}\".*" - regexList.add([matches: regex]); - } - } - return regexList + )) } private String getMatchType() { diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy index 452cc758ed..4674441a09 100755 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy @@ -161,7 +161,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { } body """ { - "name": "${value(client('Jan'), server('Honza'))}" + "name": "Jan" } """ } @@ -270,20 +270,22 @@ class WiremockGroovyDslSpec extends WiremockSpec { then: new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText(''' { - "request": { - "method": "GET", - "urlPattern": "/[0-9]{2}", - "bodyPatterns": [ - {"matches": ".*personalId\\":.?\\"^[0-9]{11}$\\".*"} - ] - }, - "response": { - "status": 200, - "body": "{\\"name\\":\\"Jan\\"}", - "headers": { - "Content-Type": "text/plain" - } - } + "request": { + "method": "GET", + "urlPattern": "/[0-9]{2}", + "bodyPatterns": [ + { + "matches":"\\\\{\\"personalId\\":\\"^[0-9]{11}$\\"\\\\}" + } + ] + }, + "response": { + "status": 200, + "body": "{\\"name\\":\\"Jan\\"}", + "headers": { + "Content-Type": "text/plain" + } + } } ''') and: @@ -334,8 +336,9 @@ class WiremockGroovyDslSpec extends WiremockSpec { }, "url": "/fraudcheck", "bodyPatterns": [ - {"matches": ".*clientPesel\\":.?\\"[0-9]{10}\\".*"}, - {"matches": ".*loanAmount\\":.?\\"123.123\\".*"} + { + "matches": "\\\\{\\"clientPesel\\":\\"[0-9]{10}\\",\\"loanAmount\\":123.123\\\\}" + } ] }, "response": { @@ -655,78 +658,10 @@ class WiremockGroovyDslSpec extends WiremockSpec { }, "X-Custom-Header": { "matches": "^.*2134.*$" - } - } - } - ''') - } - - def 'should convert groovy dsl stub with rich tree Body as String to wiremock stub for the client side'() { - given: - GroovyDsl groovyDsl = GroovyDsl.make { - request { - method('GET') - url $(client(~/\/[0-9]{2}/), server('/12')) - body """\ - { - "personalId": "${value(client(regex('[0-9]{11}')), server('57593728525'))}", - "firstName": "${value(client(regex('.*')), server('Bruce'))}", - "lastName": "${value(client(regex('.*')), server('Lee'))}", - "birthDate": "${value(client(regex('[0-9]{4}-[0-9]{2}-[0-9]{2}')), server('1985-12-12'))}", - "errors": [ - { - "propertyName": "${value(client(regex('[0-9]{2}')), server('04'))}", - "providerValue": "Test" - }, - { - "propertyName": "${value(client(regex('[0-9]{2}')), server('08'))}", - "providerValue": "Test" - } - ] - } - """ - } - response { - status 200 - body("""\ - { - "name": "Jan" - } - """ - ) - headers { - header 'Content-Type': 'text/plain' - } - } - } - when: - String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockClientStub() - then: - new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText(''' - { - "request": { - "method": "GET", - "urlPattern": "/[0-9]{2}", - "bodyPatterns": [ - {"matches": ".*birthDate\\":.?\\"[0-9]{4}-[0-9]{2}-[0-9]{2}\\".*"}, - {"matches": ".*propertyName\\":.?\\"[0-9]{2}\\".*"}, - {"matches": ".*providerValue\\":.?\\"Test\\".*"}, - {"matches": ".*propertyName\\":.?\\"[0-9]{2}\\".*"}, - {"matches": ".*providerValue\\":.?\\"Test\\".*"}, - {"matches": ".*firstName\\":.?\\".*\\".*"}, - {"matches": ".*lastName\\":.?\\".*\\".*"}, - {"matches": ".*personalId\\":.?\\"[0-9]{11}\\".*"} - ] - }, - "response": { - "status": 200, - "body": "{\\"name\\":\\"Jan\\"}", - "headers": { - "Content-Type": "text/plain" - } - } - } - ''') + } + } + } + ''') } String toJsonString(value) { From 72b00f6ac86b508709d45612cc3fb32e46ce3996 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Fri, 12 Jun 2015 13:40:54 +0200 Subject: [PATCH 028/119] [#79] Fixed the bug with exception with a json and map --- .../builder/SpockMethodBodyBuilder.groovy | 24 ++++++++------- .../builder/SpockMethodBuilderSpec.groovy | 30 +++++++++++++++++++ 2 files changed, 44 insertions(+), 10 deletions(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy index 41734b0a3a..3d44123212 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy @@ -3,6 +3,7 @@ package io.codearte.accurest.builder import groovy.json.JsonOutput import groovy.transform.PackageScope import io.codearte.accurest.dsl.GroovyDsl +import io.codearte.accurest.dsl.internal.DslProperty import io.codearte.accurest.dsl.internal.ExecutionProperty import io.codearte.accurest.dsl.internal.Header import io.codearte.accurest.dsl.internal.MatchingStrategy @@ -12,7 +13,6 @@ import io.codearte.accurest.dsl.internal.Response import io.codearte.accurest.dsl.internal.UrlPath import java.util.regex.Pattern - /** * @author Jakub Kubrynski */ @@ -102,9 +102,7 @@ class SpockMethodBodyBuilder { matchingStrategy.serverValue.toString() } - private void processBodyElement(BlockBuilder blockBuilder, String rootProperty, def element) { - def value = element.value - String property = rootProperty + "." + element.key + private void processBodyElement(BlockBuilder blockBuilder, String property, def value) { if (value instanceof String) { if (value.startsWith('$')) { value = value.substring(1).replaceAll('\\$value', "responseBody$property") @@ -114,10 +112,14 @@ class SpockMethodBodyBuilder { } } else if (value instanceof Map) { processMapElement(value, blockBuilder, property) + }else if (value instanceof Map.Entry) { + processEntryElement(blockBuilder, property, value) } else if (value instanceof List) { processArrayElements(value, property, blockBuilder) } else if (value instanceof Pattern) { blockBuilder.addLine("responseBody$property ==~ java.util.regex.Pattern.compile('${value}')") + } else if (value instanceof DslProperty) { + processBodyElement(blockBuilder, property, value.serverValue) } else if (value instanceof ExecutionProperty) { ExecutionProperty exec = (ExecutionProperty) value blockBuilder.addLine("${exec.insertValue("responseBody$property")}") @@ -127,18 +129,20 @@ class SpockMethodBodyBuilder { } private void processMapElement(def value, BlockBuilder blockBuilder, String property) { - value.each { entry -> processBodyElement(blockBuilder, property, entry) } + value.each { entry -> processEntryElement(blockBuilder, property, entry) } + } + + private def processEntryElement(BlockBuilder blockBuilder, String property, def entry) { + return processBodyElement(blockBuilder, property + "." + entry.key, entry.value) } private void processArrayElements(List responseBody, String property, BlockBuilder blockBuilder) { responseBody.eachWithIndex { listElement, listIndex -> - listElement.each { - entry -> processBodyElement(blockBuilder, property + "[$listIndex]", entry) + listElement.each { entry -> + String prop = "$property[$listIndex]" ?: '' + processBodyElement(blockBuilder, prop, entry) } } } - private void processClosure(Closure value, BlockBuilder blockBuilder, String property) { - blockBuilder.addLine() - } } diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy index 0aff49357f..a2d03358a2 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy @@ -1,6 +1,7 @@ package io.codearte.accurest.builder import io.codearte.accurest.dsl.GroovyDsl +import spock.lang.Issue import spock.lang.Specification /** @@ -32,6 +33,35 @@ class SpockMethodBuilderSpec extends Specification { blockBuilder.toString().contains("responseBody.property2 == \"b\"") } + @Issue("#79") + def "should generate assertions for simple response body constructed from map"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "GET" + url "test" + } + response { + status 200 + body ( + property1: 'a', + property2: [ + [a: 'sth'], + [b: 'sthElse'] + ] + ) + } + } + SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains("responseBody.property1 == \"a\"") + blockBuilder.toString().contains("responseBody.property2[0].a == \"sth\"") + blockBuilder.toString().contains("responseBody.property2[1].b == \"sthElse\"") + } + def "should generate assertions for array in response body"() { given: GroovyDsl contractDsl = GroovyDsl.make { From 1cabe971e477963dd8eba1331ab94c2a1d79c458 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Fri, 12 Jun 2015 14:57:25 +0200 Subject: [PATCH 029/119] [#79] Fixed the way wiremock stubs are built --- .../dsl/BaseWiremockStubStrategy.groovy | 10 +++- .../accurest/dsl/WiremockGroovyDslSpec.groovy | 48 +++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/BaseWiremockStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/BaseWiremockStubStrategy.groovy index 77866f1437..32bc831f1b 100755 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/BaseWiremockStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/BaseWiremockStubStrategy.groovy @@ -3,8 +3,10 @@ import groovy.json.JsonOutput import groovy.json.JsonSlurper import groovy.transform.TypeChecked import groovy.xml.XmlUtil +import io.codearte.accurest.dsl.internal.DslProperty import io.codearte.accurest.dsl.internal.Header import io.codearte.accurest.dsl.internal.Headers +import io.codearte.accurest.util.JsonConverter import java.util.regex.Pattern @@ -12,6 +14,11 @@ import static groovy.json.StringEscapeUtils.escapeJava @TypeChecked abstract class BaseWiremockStubStrategy { + + private static Closure transform = { + it instanceof DslProperty ? JsonConverter.transformValues(it.clientValue, transform) : it + } + protected Map buildClientRequestHeadersSection(Headers headers) { if (!headers) { return null @@ -62,6 +69,7 @@ abstract class BaseWiremockStubStrategy { } protected String parseBody(Map body) { - return JsonOutput.toJson(body) + def transformedMap = JsonConverter.transformValues(body, transform) + return JsonOutput.toJson(transformedMap) } } diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy index 7d8e5f1d49..c8767551b4 100755 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy @@ -2,6 +2,7 @@ package io.codearte.accurest.dsl import groovy.json.JsonBuilder import groovy.json.JsonSlurper +import spock.lang.Issue class WiremockGroovyDslSpec extends WiremockSpec { @@ -53,6 +54,53 @@ class WiremockGroovyDslSpec extends WiremockSpec { stubMappingIsValidWiremockStub(wiremockStub) } + @Issue("#79") + def 'should convert groovy dsl stub to wiremock stub for the client side with a body containing a map'() { + given: + GroovyDsl groovyDsl = GroovyDsl.make { + request { + method 'GET' + url '/ingredients' + headers { + header 'Content-Type': 'application/vnd.pl.devoxx.aggregatr.v1+json' + } + } + response { + status 200 + body( + ingredients: [ + [type: 'MALT', quantity: 100], + [type: 'WATER', quantity: 200], + [type: 'HOP', quantity: 300], + [type: 'YIEST', quantity: 400] + ] + ) + } + } + when: + String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockClientStub() + then: + new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText(''' +{ + "request": { + "method": "GET", + "headers": { + "Content-Type": { + "equalTo": "application/vnd.pl.devoxx.aggregatr.v1+json" + } + }, + "url": "/ingredients" + }, + "response": { + "status": 200, + "body": "{\\"ingredients\\":[{\\"type\\":\\"MALT\\",\\"quantity\\":100},{\\"type\\":\\"WATER\\",\\"quantity\\":200},{\\"type\\":\\"HOP\\",\\"quantity\\":300},{\\"type\\":\\"YIEST\\",\\"quantity\\":400}]}" + } +} +''') + and: + stubMappingIsValidWiremockStub(wiremockStub) + } + def 'should convert groovy dsl stub with Body as String to wiremock stub for the client side'() { given: GroovyDsl groovyDsl = GroovyDsl.make { From a0c6f7959c7391230c74cc8acc4cb3ee43fda69b Mon Sep 17 00:00:00 2001 From: Denis Stepanov Date: Fri, 12 Jun 2015 15:20:16 +0200 Subject: [PATCH 030/119] Revert "Implement selecting compare method by content type" This reverts commit a5502b35e27dffdf814b3e364a57023bc314ef75. --- .../dsl/WiremockRequestStubStrategy.groovy | 55 +++++++++++-------- .../accurest/dsl/internal/Body.groovy | 8 --- 2 files changed, 31 insertions(+), 32 deletions(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy index 31eb05300a..3390b6456b 100755 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy @@ -2,6 +2,7 @@ package io.codearte.accurest.dsl import groovy.transform.PackageScope import groovy.transform.TypeChecked import io.codearte.accurest.dsl.internal.ClientRequest +import io.codearte.accurest.dsl.internal.Header import io.codearte.accurest.dsl.internal.MatchingStrategy import io.codearte.accurest.dsl.internal.QueryParameter import io.codearte.accurest.dsl.internal.QueryParameters @@ -79,35 +80,32 @@ class WiremockRequestStubStrategy extends BaseWiremockStubStrategy { if (body == null) { return [:] } - if (clientRequest.body?.containsPattern) { - return [bodyPatterns: [[matches: escapeBodyForJson(body)]]] - } - return [bodyPatterns: [[(getMatchType()): parseBody(body)]]] - } - - private Object escapeBodyForJson(Object body) { - return parseBody(convertJsonStructureToObjectUnderstandingStructure(body, - { it instanceof Pattern }, - { String json -> json.collect { - switch(it) { - case ('{'): return '\\{' - case ('}'): return '\\}' - default: return it + if (containsRegex(body)) { + return [bodyPatterns: [[matches: parseBody(convertJsonStructureToObjectUnderstandingStructure(body, + { it instanceof Pattern }, + { String json -> json.collect { + switch(it) { + case ('{'): return '\\{' + case ('}'): return '\\}' + default: return it + } + } .join('') + }, + { LinkedList list, String json -> + return json.replaceAll(TEMPORARY_PATTERN_HOLDER, { String a, String[] b -> list.pop() }) } - } .join('') - }, - { LinkedList list, String json -> - return json.replaceAll(TEMPORARY_PATTERN_HOLDER, { String a, String[] b -> list.pop() }) - } - )) + ))]]] + } + + return [bodyPatterns: [[(getCompareType()): parseBody(body)]]] } - private String getMatchType() { - String content = request.headers?.entries.find { it.name == "Content-Type" } ?.clientValue?.toString() - if (content?.endsWith("json")) { + private String getCompareType() { + Header contentType = request.headers?.entries.find { it.name == "Content-Type" } + if (contentType && contentType.clientValue.toString().endsWith("json")) { return "equalToJson" } - if (content?.endsWith("xml")) { + if (contentType && contentType.clientValue.toString().endsWith("xml")) { return "equalToXml" } return "equalTo" @@ -117,4 +115,13 @@ class WiremockRequestStubStrategy extends BaseWiremockStubStrategy { return body } + boolean containsRegex(Object bodyObject) { + String bodyString = bodyObject as String + return (bodyString =~ /\^.*\$/).find() + } + + boolean containsRegex(Map map) { + return map.values().any { it instanceof Pattern } + } + } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Body.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Body.groovy index 78813a74a8..c68380f0f2 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Body.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Body.groovy @@ -19,8 +19,6 @@ class Body extends DslProperty { private static final Pattern TEMPORARY_PATTERN_HOLDER = Pattern.compile('REGEXP>>(.*)<<') private static final String JSON_VALUE_PATTERN_FOR_REGEX = 'REGEXP>>%s<<' - boolean containsPattern - Body(Map body) { super(extractValue(body, {it.clientValue}), extractValue(body, {it.serverValue})) } @@ -41,18 +39,12 @@ class Body extends DslProperty { Body(GString bodyAsValue) { super(extractValue(bodyAsValue, {it.clientValue}), extractValue(bodyAsValue, {it.serverValue})) - containsPattern = containsPattern(bodyAsValue) } Body(DslProperty bodyAsValue) { super(bodyAsValue.clientValue, bodyAsValue.serverValue) } - private boolean containsPattern(GString bodyAsValue) { - return bodyAsValue.values.collect { it instanceof DslProperty ? it.clientValue : it } - .find { it instanceof Pattern } - } - /** * Due to the fact that we allow users to have a body with GString and different values inside * we need to be prepared that they pass regexps around both on client and server side. From 080aae6060be1202d92deabdddf11baf97a1b3a3 Mon Sep 17 00:00:00 2001 From: Denis Stepanov Date: Fri, 12 Jun 2015 15:20:49 +0200 Subject: [PATCH 031/119] Revert "Implement selecting compare method by content type" This reverts commit 58b47fd6b4da6641503e34fac41840f22084ad89. --- .../dsl/WiremockRequestStubStrategy.groovy | 15 +--- .../accurest/dsl/internal/Body.groovy | 51 ++--------- .../accurest/dsl/WiremockGroovyDslSpec.groovy | 90 ------------------- 3 files changed, 7 insertions(+), 149 deletions(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy index 3390b6456b..298cc1b2ec 100755 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy @@ -2,7 +2,6 @@ package io.codearte.accurest.dsl import groovy.transform.PackageScope import groovy.transform.TypeChecked import io.codearte.accurest.dsl.internal.ClientRequest -import io.codearte.accurest.dsl.internal.Header import io.codearte.accurest.dsl.internal.MatchingStrategy import io.codearte.accurest.dsl.internal.QueryParameter import io.codearte.accurest.dsl.internal.QueryParameters @@ -96,19 +95,7 @@ class WiremockRequestStubStrategy extends BaseWiremockStubStrategy { } ))]]] } - - return [bodyPatterns: [[(getCompareType()): parseBody(body)]]] - } - - private String getCompareType() { - Header contentType = request.headers?.entries.find { it.name == "Content-Type" } - if (contentType && contentType.clientValue.toString().endsWith("json")) { - return "equalToJson" - } - if (contentType && contentType.clientValue.toString().endsWith("xml")) { - return "equalToXml" - } - return "equalTo" + return [bodyPatterns: [[equalTo: parseBody(body)]]] } protected String parseBody(Object body) { diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Body.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Body.groovy index c68380f0f2..315135d939 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Body.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Body.groovy @@ -1,6 +1,5 @@ package io.codearte.accurest.dsl.internal -import groovy.json.JsonException import groovy.json.JsonSlurper import groovy.transform.EqualsAndHashCode import groovy.transform.ToString @@ -10,8 +9,6 @@ import org.codehaus.groovy.runtime.GStringImpl import java.util.regex.Matcher import java.util.regex.Pattern -import static org.apache.commons.lang3.StringEscapeUtils.escapeXml11 - @ToString(includePackage = false, includeFields = true, includeNames = true) @EqualsAndHashCode(includeFields = true) class Body extends DslProperty { @@ -58,51 +55,15 @@ class Body extends DslProperty { * @return JSON structure with replaced client / server side parts */ private static Object extractValue(GString bodyAsValue, Closure valueProvider) { - try { - return extractValueForJSON(bodyAsValue, valueProvider) - } catch(JsonException e) { - // Not a JSON format - return extractValueForXML(bodyAsValue, valueProvider) - } - return bodyAsValue - } - - private static Object extractValueForJSON(GString bodyAsValue, Closure valueProvider) { - GString transformedString = new GStringImpl( - bodyAsValue.values.collect { transformJSONStringValue(it, valueProvider) } as Object[], - bodyAsValue.strings.clone() - ) - def parsedJson = new JsonSlurper().parseText(transformedString) + GString gString = new GStringImpl(bodyAsValue.values.clone(), bodyAsValue.strings.clone()) + Object[] values = bodyAsValue.values.collect { it instanceof DslProperty ? valueProvider(it) : it } as Object[] + Object[] valuesWithRegexpsAsTransformedStrings = values.collect { + it instanceof Pattern ? String.format(JSON_VALUE_PATTERN_FOR_REGEX, it.toString()) : it + } as Object[] + def parsedJson = new JsonSlurper().parseText(new GStringImpl(valuesWithRegexpsAsTransformedStrings, gString.strings)) return convertAllTemporaryRegexPlaceholdersBackToPatterns(parsedJson) } - private static GStringImpl extractValueForXML(GString bodyAsValue, Closure valueProvider) { - return new GStringImpl( - bodyAsValue.values.collect { transformXMLStringValue(it, valueProvider) } as Object[], - bodyAsValue.strings.clone() - ) - } - - private static String transformJSONStringValue(Object obj, Closure valueProvider) { - return obj.toString() - } - - private static String transformJSONStringValue(DslProperty dslProperty, Closure valueProvider) { - return transformJSONStringValue(valueProvider(dslProperty), valueProvider) - } - - private static String transformJSONStringValue(Pattern pattern, Closure valueProvider) { - return String.format(JSON_VALUE_PATTERN_FOR_REGEX, pattern.pattern()) - } - - private static String transformXMLStringValue(Object obj, Closure valueProvider) { - return escapeXml11(obj.toString()) - } - - private static String transformXMLStringValue(DslProperty dslProperty, Closure valueProvider) { - return transformXMLStringValue(valueProvider(dslProperty), valueProvider) - } - private static Object convertAllTemporaryRegexPlaceholdersBackToPatterns(parsedJson) { JsonConverter.transformValues(parsedJson, { Object value -> if (value instanceof String) { diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy index 4674441a09..7d8e5f1d49 100755 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy @@ -150,96 +150,6 @@ class WiremockGroovyDslSpec extends WiremockSpec { stubMappingIsValidWiremockStub(wiremockStub) } - def 'should use equalToJson when content type ends with json'() { - given: - GroovyDsl groovyDsl = GroovyDsl.make { - request { - method 'GET' - url "/users" - headers { - header "Content-Type", "customtype/json" - } - body """ - { - "name": "Jan" - } - """ - } - response { - status 200 - } - } - when: - String json = toWiremockClientJsonStub(groovyDsl) - then: - parseJson(json) == parseJson(''' - { - "request": { - "method": "GET", - "url": "/users", - "headers": { - "Content-Type": { - "equalTo": "customtype/json" - } - }, - "bodyPatterns": [ - { - "equalToJson":"{\\"name\\":\\"Jan\\"}" - } - ] - }, - "response": { - "status": 200 - } - } - ''') - and: - stubMappingIsValidWiremockStub(json) - } - - def 'should use equalToXml when content type ends with xml'() { - given: - GroovyDsl groovyDsl = GroovyDsl.make { - request { - method 'GET' - url "/users" - headers { - header "Content-Type", "customtype/xml" - } - body """${value(client('Jozo'), server('Denis'))}${value(client(""), server('1234567890'))}""" - } - response { - status 200 - } - } - when: - String json = toWiremockClientJsonStub(groovyDsl) - then: - parseJson(json) == parseJson(''' - { - "request": { - "method": "GET", - "url": "/users", - "headers": { - "Content-Type": { - "equalTo": "customtype/xml" - } - }, - "bodyPatterns": [ - { - "equalToXml":"Jozo<test>" - } - ] - }, - "response": { - "status": 200 - } - } - ''') - and: - stubMappingIsValidWiremockStub(json) - } - def 'should convert groovy dsl stub with regexp Body as String to wiremock stub for the client side'() { given: GroovyDsl groovyDsl = GroovyDsl.make { From b2887e9588ca82826c05a111bcf3ad9a6f1928f5 Mon Sep 17 00:00:00 2001 From: Denis Stepanov Date: Fri, 12 Jun 2015 15:23:02 +0200 Subject: [PATCH 032/119] Revert changes --- .../test/groovy/io/codearte/accurest/dsl/WiremockSpec.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockSpec.groovy index ab1133c9ae..796e603c11 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockSpec.groovy @@ -5,7 +5,7 @@ import spock.lang.Specification import java.util.regex.Pattern -abstract class WiremockSpec extends Specification { +class WiremockSpec extends Specification { void stubMappingIsValidWiremockStub(String mappingDefinition) { StubMapping stubMapping = StubMapping.buildFrom(mappingDefinition) From 1cca744b56cf8a786969704d063369d1162dffbd Mon Sep 17 00:00:00 2001 From: Denis Stepanov Date: Fri, 12 Jun 2015 15:25:46 +0200 Subject: [PATCH 033/119] Fix windows bug --- .../src/main/groovy/io/codearte/accurest/util/NamesUtil.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/util/NamesUtil.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/NamesUtil.groovy index 4817cc4816..a15bd1b5dc 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/util/NamesUtil.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/NamesUtil.groovy @@ -40,6 +40,6 @@ class NamesUtil { } static String packageToDirectory(String packageName) { - return packageName.replaceAll('\\.', File.separator) + return packageName.replace('.' as char, File.separatorChar) } } From 118b63c2a036440dd3c2f02823f0cbd0cd6c8b3b Mon Sep 17 00:00:00 2001 From: Denis Stepanov Date: Fri, 12 Jun 2015 11:47:15 +0200 Subject: [PATCH 034/119] Body matching by content type and client-side XML support --- .../DslToWiremockClientConverterSpec.groovy | 2 +- .../builder/SpockMethodBodyBuilder.groovy | 34 +- .../dsl/BaseWiremockStubStrategy.groovy | 70 ++- .../dsl/WiremockRequestStubStrategy.groovy | 94 ++- .../dsl/WiremockResponseStubStrategy.groovy | 15 +- .../accurest/dsl/internal/Body.groovy | 55 +- .../dsl/internal/MatchingStrategy.groovy | 45 +- .../dsl/internal/QueryParameters.groovy | 16 - .../accurest/dsl/internal/Request.groovy | 50 ++ .../codearte/accurest/util/ContentType.groovy | 5 + .../accurest/util/ContentUtils.groovy | 192 ++++++ .../accurest/dsl/WiremockGroovyDslSpec.groovy | 557 +++++++++++++++--- .../codearte/accurest/dsl/WiremockSpec.groovy | 2 +- .../plugin/BasicFunctionalSpec.groovy | 2 +- 14 files changed, 897 insertions(+), 242 deletions(-) create mode 100644 accurest-core/src/main/groovy/io/codearte/accurest/util/ContentType.groovy create mode 100644 accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy diff --git a/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/DslToWiremockClientConverterSpec.groovy b/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/DslToWiremockClientConverterSpec.groovy index 2f20546014..edf11a256f 100755 --- a/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/DslToWiremockClientConverterSpec.groovy +++ b/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/DslToWiremockClientConverterSpec.groovy @@ -85,7 +85,7 @@ class DslToWiremockClientConverterSpec extends Specification { "method":"PUT", "url":"/api/12", "bodyPatterns": [ - { "equalTo": "[{\\"created_at\\":\\"Sat Jul 26 09:38:57 +0000 2014\\",\\"id\\":492967299297845248,\\"id_str\\":\\"492967299297845248\\",\\"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\\"},\\"text\\":\\"Gonna see you at Warsaw\\"}]" } + { "equalToJson": "[{\\"created_at\\":\\"Sat Jul 26 09:38:57 +0000 2014\\",\\"id\\":492967299297845248,\\"id_str\\":\\"492967299297845248\\",\\"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\\"},\\"text\\":\\"Gonna see you at Warsaw\\"}]" } ], "headers": { "Content-Type": { diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy index 3d44123212..544abb98b1 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy @@ -11,8 +11,14 @@ import io.codearte.accurest.dsl.internal.QueryParameter import io.codearte.accurest.dsl.internal.Request import io.codearte.accurest.dsl.internal.Response import io.codearte.accurest.dsl.internal.UrlPath +import io.codearte.accurest.util.ContentType import java.util.regex.Pattern + +import static io.codearte.accurest.util.ContentUtils.extractValue +import static io.codearte.accurest.util.ContentUtils.recognizeContentTypeFromHeader +import static io.codearte.accurest.util.ContentUtils.recognizeContentTypeFromContent + /** * @author Jakub Kubrynski */ @@ -36,7 +42,11 @@ class SpockMethodBodyBuilder { addLine(".header('${header.name}', '${header.serverValue}')") } if (request.body) { - String matches = new JsonOutput().toJson(request.body.serverValue) + Object bodyValue = request.body.serverValue + if (bodyValue instanceof GString) { + bodyValue = extractValue(bodyValue, {DslProperty dslProperty -> dslProperty.serverValue}) + } + String matches = new JsonOutput().toJson(bodyValue) addLine(".body('$matches')") } @@ -61,12 +71,24 @@ class SpockMethodBodyBuilder { if (response.body) { endBlock() addLine('and:').startBlock() - addLine('def responseBody = new JsonSlurper().parseText(response.body.asString())') def responseBody = response.body.serverValue - if (responseBody instanceof List) { - processArrayElements(responseBody, "", blockBuilder) - } else { - processMapElement(responseBody, blockBuilder, "") + ContentType contentType = recognizeContentTypeFromHeader(response.headers) + if (contentType == ContentType.UNKNOWN) { + contentType = recognizeContentTypeFromContent(responseBody) + } + if (responseBody instanceof GString) { + responseBody = extractValue(responseBody, contentType, { DslProperty dslProperty -> dslProperty.serverValue }) + } + if (contentType == ContentType.JSON) { + addLine('def responseBody = new JsonSlurper().parseText(response.body.asString())') + if (responseBody instanceof List) { + processArrayElements(responseBody, "", blockBuilder) + } else { + processMapElement(responseBody, blockBuilder, "") + } + } else if (contentType == ContentType.XML) { + addLine('def responseBody = new XmlSlurper().parseText(response.body.asString())') + // TODO xml validation } } endBlock() diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/BaseWiremockStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/BaseWiremockStubStrategy.groovy index 32bc831f1b..a5c100341b 100755 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/BaseWiremockStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/BaseWiremockStubStrategy.groovy @@ -1,22 +1,22 @@ package io.codearte.accurest.dsl -import groovy.json.JsonOutput -import groovy.json.JsonSlurper + +import groovy.json.JsonBuilder import groovy.transform.TypeChecked -import groovy.xml.XmlUtil import io.codearte.accurest.dsl.internal.DslProperty import io.codearte.accurest.dsl.internal.Header import io.codearte.accurest.dsl.internal.Headers -import io.codearte.accurest.util.JsonConverter +import io.codearte.accurest.util.ContentType import java.util.regex.Pattern -import static groovy.json.StringEscapeUtils.escapeJava +import static io.codearte.accurest.util.ContentUtils.extractValue +import static io.codearte.accurest.util.JsonConverter.transformValues @TypeChecked abstract class BaseWiremockStubStrategy { private static Closure transform = { - it instanceof DslProperty ? JsonConverter.transformValues(it.clientValue, transform) : it + it instanceof DslProperty ? transformValues(it.clientValue, transform) : it } protected Map buildClientRequestHeadersSection(Headers headers) { @@ -33,43 +33,49 @@ abstract class BaseWiremockStubStrategy { return null } return headers.entries.collectEntries { Header entry -> - [(entry.name) : entry.clientValue] + [(entry.name): entry.clientValue] } } protected Map parseHeader(String entryKey, Object entry) { - return [(entryKey): [equalTo : entry]] + return [(entryKey): [equalTo: entry]] } protected Map parseHeader(String entryKey, String entry) { - return [(entryKey): [equalTo : entry]] + return [(entryKey): [equalTo: entry]] } protected Map parseHeader(String entryKey, Pattern entry) { - return [(entryKey): [matches : entry.pattern()]] + return [(entryKey): [matches: entry.pattern()]] } - protected String parseBody(Object body) { - String bodyAsString = body as String - try { - def json = new JsonSlurper().parseText(bodyAsString) - return escapeJava(JsonOutput.toJson(bodyAsString)) - } catch (Exception jsonException) { - try { - def xml = new XmlSlurper().parseText(bodyAsString) - return escapeJava(XmlUtil.serialize(bodyAsString)) - } catch (Exception xmlException) { - return escapeJava(bodyAsString) - } - } - } + public String parseBody(Object value, ContentType contentType) { + return parseBody(value.toString(), contentType) + } - protected String parseBody(List body) { - return JsonOutput.toJson(body) - } + public String parseBody(Map map, ContentType contentType) { + def transformedMap = transformValues(map, transform) + return parseBody(toJson(transformedMap), contentType) + } - protected String parseBody(Map body) { - def transformedMap = JsonConverter.transformValues(body, transform) - return JsonOutput.toJson(transformedMap) - } -} + public String parseBody(List list, ContentType contentType) { + return parseBody(toJson(list), contentType) + } + + public String parseBody(GString value, ContentType contentType) { + Object processedValue = extractValue(value, contentType, { DslProperty dslProperty -> dslProperty.clientValue }) + if (processedValue instanceof GString) { + return parseBody(processedValue.toString(), contentType) + } + return parseBody(processedValue, contentType) + } + + public String parseBody(String value, ContentType contentType) { + return value + } + + private static toJson(Object value) { + return new JsonBuilder(value).toString() + } + +} \ No newline at end of file diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy index 298cc1b2ec..f9bc9bdc23 100755 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy @@ -1,16 +1,23 @@ package io.codearte.accurest.dsl + import groovy.transform.PackageScope import groovy.transform.TypeChecked +import io.codearte.accurest.dsl.internal.Body import io.codearte.accurest.dsl.internal.ClientRequest +import io.codearte.accurest.dsl.internal.DslProperty import io.codearte.accurest.dsl.internal.MatchingStrategy import io.codearte.accurest.dsl.internal.QueryParameter import io.codearte.accurest.dsl.internal.QueryParameters import io.codearte.accurest.dsl.internal.Request +import io.codearte.accurest.util.ContentType import java.util.regex.Pattern -import static io.codearte.accurest.dsl.internal.JsonStructureConverter.TEMPORARY_PATTERN_HOLDER -import static io.codearte.accurest.dsl.internal.JsonStructureConverter.convertJsonStructureToObjectUnderstandingStructure +import static io.codearte.accurest.util.ContentUtils.extractValue +import static io.codearte.accurest.util.ContentUtils.recognizeContentTypeFromHeader +import static io.codearte.accurest.util.ContentUtils.getEqualsTypeFromContentType +import static io.codearte.accurest.util.ContentUtils.recognizeContentTypeFromContent +import static io.codearte.accurest.util.ContentUtils.recognizeContentTypeFromMatchingStrategy @TypeChecked @PackageScope @@ -29,7 +36,7 @@ class WiremockRequestStubStrategy extends BaseWiremockStubStrategy { private Map buildRequestContent(ClientRequest request) { return ([method : request?.method?.clientValue, - headers : buildClientRequestHeadersSection(request.headers) + headers : buildClientRequestHeadersSection(request.headers) ] << appendUrl(request) << appendQueryParameters(request) << appendBody(request)).findAll { it.value } } @@ -75,40 +82,69 @@ class WiremockRequestStubStrategy extends BaseWiremockStubStrategy { } private Map appendBody(ClientRequest clientRequest) { - Object body = clientRequest?.body?.clientValue - if (body == null) { - return [:] + return clientRequest.body? appendBody(clientRequest.body) : [:] + } + + private Map appendBody(Body body) { + return [bodyPatterns: (appendBodyPatterns(body.clientValue))] + } + + private List> appendBodyPatterns(MatchingStrategy matchingStrategy) { + return [appendBodyPattern(matchingStrategy)] + } + + private List> appendBodyPatterns(List matchingStrategies) { + return matchingStrategies.collect { appendBodyPattern(it) } + } + + private List> appendBodyPatterns(GString gString) { + if (containsPattern(gString)) { + Object value = extractValue(gString, { DslProperty dslProperty -> dslProperty.clientValue }) + return appendBodyPatterns(extractReqexpMatching(value)) } - if (containsRegex(body)) { - return [bodyPatterns: [[matches: parseBody(convertJsonStructureToObjectUnderstandingStructure(body, - { it instanceof Pattern }, - { String json -> json.collect { - switch(it) { - case ('{'): return '\\{' - case ('}'): return '\\}' - default: return it - } - } .join('') - }, - { LinkedList list, String json -> - return json.replaceAll(TEMPORARY_PATTERN_HOLDER, { String a, String[] b -> list.pop() }) - } - ))]]] + return appendBodyPatterns(new MatchingStrategy(gString, getEqualsTypeFromContentTypeHeader())) + } + + private List> appendBodyPatterns(Object bodyValue) { + return appendBodyPatterns(new MatchingStrategy(bodyValue, MatchingStrategy.Type.EQUAL_TO)) + } + + private Map appendBodyPattern(MatchingStrategy matchingStrategy) { + MatchingStrategy.Type type = matchingStrategy.type + Object value= matchingStrategy.clientValue + ContentType contentType = recognizeContentTypeFromMatchingStrategy(type) + if (contentType == ContentType.UNKNOWN && type == MatchingStrategy.Type.EQUAL_TO) { + contentType = recognizeContentTypeFromContent(value) + type = getEqualsTypeFromContentType(contentType) } - return [bodyPatterns: [[equalTo: parseBody(body)]]] + Map result = [(type.name): parseBody(value, contentType)] + if (type == MatchingStrategy.Type.EQUAL_TO_JSON && matchingStrategy.jsonCompareMode) { + return result << [jsonCompareMode : (matchingStrategy.jsonCompareMode.toString())] + } + return result } - protected String parseBody(Object body) { - return body + private boolean containsPattern(GString bodyAsValue) { + return bodyAsValue.values.collect { it instanceof DslProperty ? it.clientValue : it } + .find { it instanceof Pattern } } - boolean containsRegex(Object bodyObject) { - String bodyString = bodyObject as String - return (bodyString =~ /\^.*\$/).find() + private List extractReqexpMatching(Object responseBodyObject) { + def matchingStrategies = new ArrayList() + responseBodyObject.each { k, v -> + if (v instanceof List) { + v.each { + matchingStrategies.addAll(extractReqexpMatching((Map)it)) + } + } else { + matchingStrategies.add(new MatchingStrategy(/.*${k}":.?"?${v}"?.*/, MatchingStrategy.Type.MATCHING)) + } + } + return matchingStrategies } - boolean containsRegex(Map map) { - return map.values().any { it instanceof Pattern } + private MatchingStrategy.Type getEqualsTypeFromContentTypeHeader() { + return getEqualsTypeFromContentType(recognizeContentTypeFromHeader(request.headers)) } } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockResponseStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockResponseStubStrategy.groovy index b517150236..a17dad9420 100755 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockResponseStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockResponseStubStrategy.groovy @@ -2,16 +2,23 @@ package io.codearte.accurest.dsl import groovy.transform.PackageScope import groovy.transform.TypeChecked import io.codearte.accurest.dsl.internal.ClientResponse +import io.codearte.accurest.dsl.internal.Request import io.codearte.accurest.dsl.internal.Response +import io.codearte.accurest.util.ContentType + +import static io.codearte.accurest.util.ContentUtils.recognizeContentTypeFromContent +import static io.codearte.accurest.util.ContentUtils.recognizeContentTypeFromHeader @TypeChecked @PackageScope class WiremockResponseStubStrategy extends BaseWiremockStubStrategy { + private final Request request private final Response response WiremockResponseStubStrategy(GroovyDsl groovyDsl) { this.response = groovyDsl.response + this.request = groovyDsl.request } @PackageScope @@ -27,6 +34,12 @@ class WiremockResponseStubStrategy extends BaseWiremockStubStrategy { private Map appendBody(ClientResponse response) { Object body = response?.body?.clientValue - return body != null ? [body: parseBody(body)] : [:] + ContentType contentType = recognizeContentTypeFromHeader(response.headers) + if (contentType == ContentType.UNKNOWN) { + contentType = recognizeContentTypeFromContent(body) + } + return body != null ? [body: parseBody(body, contentType)] : [:] } + + } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Body.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Body.groovy index 315135d939..df799d87f8 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Body.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Body.groovy @@ -1,23 +1,16 @@ package io.codearte.accurest.dsl.internal -import groovy.json.JsonSlurper +import groovy.transform.CompileStatic import groovy.transform.EqualsAndHashCode import groovy.transform.ToString -import io.codearte.accurest.util.JsonConverter -import org.codehaus.groovy.runtime.GStringImpl - -import java.util.regex.Matcher -import java.util.regex.Pattern @ToString(includePackage = false, includeFields = true, includeNames = true) @EqualsAndHashCode(includeFields = true) +@CompileStatic class Body extends DslProperty { - private static final Pattern TEMPORARY_PATTERN_HOLDER = Pattern.compile('REGEXP>>(.*)<<') - private static final String JSON_VALUE_PATTERN_FOR_REGEX = 'REGEXP>>%s<<' - Body(Map body) { - super(extractValue(body, {it.clientValue}), extractValue(body, {it.serverValue})) + super(extractValue(body, { DslProperty p -> p.clientValue}), extractValue(body, {DslProperty p -> p.serverValue})) } private static Map extractValue(Map body, Closure valueProvider) { @@ -26,8 +19,8 @@ class Body extends DslProperty { } as Map } - Body(List bodyAsList) { - super(bodyAsList.collect { it.clientValue }, bodyAsList.collect { it.serverValue }) + Body(List bodyAsList) { + super(bodyAsList.collect { DslProperty p -> p.clientValue }, bodyAsList.collect { DslProperty p -> p.serverValue }) } Body(Object bodyAsValue) { @@ -35,48 +28,16 @@ class Body extends DslProperty { } Body(GString bodyAsValue) { - super(extractValue(bodyAsValue, {it.clientValue}), extractValue(bodyAsValue, {it.serverValue})) + super(bodyAsValue, bodyAsValue) } Body(DslProperty bodyAsValue) { super(bodyAsValue.clientValue, bodyAsValue.serverValue) } - /** - * Due to the fact that we allow users to have a body with GString and different values inside - * we need to be prepared that they pass regexps around both on client and server side. - * - * In order to preserve the original JSON structure we need to convert the passed Regex patterns - * to a temporary string, then convert all to a legitimate JSON structure and then finally - * convert it back from string to a pattern. - * - * @param bodyAsValue - GString with passed values - * @param valueProvider - provider of values either for server or client side - * @return JSON structure with replaced client / server side parts - */ - private static Object extractValue(GString bodyAsValue, Closure valueProvider) { - GString gString = new GStringImpl(bodyAsValue.values.clone(), bodyAsValue.strings.clone()) - Object[] values = bodyAsValue.values.collect { it instanceof DslProperty ? valueProvider(it) : it } as Object[] - Object[] valuesWithRegexpsAsTransformedStrings = values.collect { - it instanceof Pattern ? String.format(JSON_VALUE_PATTERN_FOR_REGEX, it.toString()) : it - } as Object[] - def parsedJson = new JsonSlurper().parseText(new GStringImpl(valuesWithRegexpsAsTransformedStrings, gString.strings)) - return convertAllTemporaryRegexPlaceholdersBackToPatterns(parsedJson) + Body(MatchingStrategy matchingStrategy) { + super(matchingStrategy, matchingStrategy) } - private static Object convertAllTemporaryRegexPlaceholdersBackToPatterns(parsedJson) { - JsonConverter.transformValues(parsedJson, { Object value -> - if (value instanceof String) { - String string = (String) value - Matcher matcher = TEMPORARY_PATTERN_HOLDER.matcher(string) - if (matcher.matches()) { - String pattern = matcher[0][1] - return Pattern.compile(pattern) - } - return value - } - return value - }) - } } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/MatchingStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/MatchingStrategy.groovy index c385bb1939..e9093dc9aa 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/MatchingStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/MatchingStrategy.groovy @@ -9,26 +9,39 @@ import groovy.transform.ToString; @CompileStatic class MatchingStrategy extends DslProperty { - Type type + Type type + JSONCompareMode jsonCompareMode - MatchingStrategy(Object value, Type type) { - super(value) - this.type = type - } + MatchingStrategy(Object value, Type type) { + this(value, type, null) + } - MatchingStrategy(DslProperty value, Type type) { - super(value.clientValue, value.serverValue) - this.type = type - } + MatchingStrategy(Object value, Type type, JSONCompareMode jsonCompareMode) { + super(value) + this.type = type + this.jsonCompareMode = jsonCompareMode + } - enum Type { - EQUAL_TO("equalTo"), CONTAINS("contains"), MATCHING("matches"), NOT_MATCHING("doesNotMatch") + MatchingStrategy(DslProperty value, Type type) { + this(value, type, null) + } - final String name + MatchingStrategy(DslProperty value, Type type, JSONCompareMode jsonCompareMode) { + super(value.clientValue, value.serverValue) + this.type = type + this.jsonCompareMode = jsonCompareMode + } - Type(name) { - this.name = name - } - } + enum Type { + + EQUAL_TO("equalTo"), CONTAINS("contains"), MATCHING("matches"), NOT_MATCHING("doesNotMatch"), + EQUAL_TO_JSON("equalToJson"), EQUAL_TO_XML("equalToXml") + + final String name + + Type(name) { + this.name = name + } + } } \ No newline at end of file diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameters.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameters.groovy index eedaf5654b..0a5d6233f2 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameters.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameters.groovy @@ -20,20 +20,4 @@ class QueryParameters { parameters << new QueryParameter(parameterName, parameterValue) } - MatchingStrategy equalTo(Object value) { - return new MatchingStrategy(value, MatchingStrategy.Type.EQUAL_TO) - } - - MatchingStrategy containing(Object value) { - return new MatchingStrategy(value, MatchingStrategy.Type.CONTAINS) - } - - MatchingStrategy matching(Object value) { - return new MatchingStrategy(value, MatchingStrategy.Type.MATCHING) - } - - MatchingStrategy notMatching(Object value) { - return new MatchingStrategy(value, MatchingStrategy.Type.NOT_MATCHING) - } - } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Request.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Request.groovy index dab8f873f2..67b9dde09f 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Request.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Request.groovy @@ -3,6 +3,7 @@ import groovy.transform.CompileStatic import groovy.transform.EqualsAndHashCode import groovy.transform.ToString import groovy.transform.TypeChecked +import groovy.xml.MarkupBuilder @TypeChecked @EqualsAndHashCode @@ -88,6 +89,10 @@ class Request extends Common { this.body = new Body(convertObjectsToDslProperties(body)) } + void body(DslProperty dslProperty) { + this.body = new Body(dslProperty) + } + void body(Object bodyAsValue) { this.body = new Body(bodyAsValue) } @@ -95,6 +100,51 @@ class Request extends Common { Body getBody() { return body } + + MatchingStrategy equalTo(Object value) { + return new MatchingStrategy(value, MatchingStrategy.Type.EQUAL_TO) + } + + MatchingStrategy containing(Object value) { + return new MatchingStrategy(value, MatchingStrategy.Type.CONTAINS) + } + + MatchingStrategy matching(Object value) { + return new MatchingStrategy(value, MatchingStrategy.Type.MATCHING) + } + + MatchingStrategy notMatching(Object value) { + return new MatchingStrategy(value, MatchingStrategy.Type.NOT_MATCHING) + } + + MatchingStrategy equalToXml(Object value) { + return new MatchingStrategy(value, MatchingStrategy.Type.EQUAL_TO_XML) + } + + MatchingStrategy equalToJson(Object value) { + return new MatchingStrategy(value, MatchingStrategy.Type.EQUAL_TO_JSON) + } + + MatchingStrategy equalToJson(Object value, JSONCompareMode jsonCompareMode) { + return new MatchingStrategy(value, MatchingStrategy.Type.EQUAL_TO_JSON) + } + + MatchingStrategy equalToJsonStrictly(Object value) { + return new MatchingStrategy(value, MatchingStrategy.Type.EQUAL_TO_JSON, JSONCompareMode.STRICT) + } + + MatchingStrategy equalToJsonLeniently(Object value) { + return new MatchingStrategy(value, MatchingStrategy.Type.EQUAL_TO_JSON, JSONCompareMode.LENIENT) + } + + MatchingStrategy equalToJsonNonExtensibly(Object value) { + return new MatchingStrategy(value, MatchingStrategy.Type.EQUAL_TO_JSON, JSONCompareMode.NON_EXTENSIBLE) + } + + MatchingStrategy equalToJsonWithStrictOrder(Object value) { + return new MatchingStrategy(value, MatchingStrategy.Type.EQUAL_TO_JSON, JSONCompareMode.STRICT_ORDER) + } + } @CompileStatic diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentType.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentType.groovy new file mode 100644 index 0000000000..142c93a945 --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentType.groovy @@ -0,0 +1,5 @@ +package io.codearte.accurest.util + +enum ContentType { + JSON, XML, UNKNOWN +} \ No newline at end of file diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy new file mode 100644 index 0000000000..1a187595b9 --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy @@ -0,0 +1,192 @@ +package io.codearte.accurest.util +import groovy.json.JsonException +import groovy.json.JsonSlurper +import groovy.transform.TypeChecked +import io.codearte.accurest.dsl.internal.DslProperty +import io.codearte.accurest.dsl.internal.Headers +import io.codearte.accurest.dsl.internal.MatchingStrategy +import org.codehaus.groovy.runtime.GStringImpl + +import java.util.regex.Matcher +import java.util.regex.Pattern + +import static org.apache.commons.lang3.StringEscapeUtils.escapeJson +import static org.apache.commons.lang3.StringEscapeUtils.escapeXml11 + +@TypeChecked +class ContentUtils { + + private static final Pattern TEMPORARY_PATTERN_HOLDER = Pattern.compile('REGEXP>>(.*)<<') + private static final String JSON_VALUE_PATTERN_FOR_REGEX = 'REGEXP>>%s<<' + + /** + * Due to the fact that we allow users to have a body with GString and different values inside + * we need to be prepared that they pass regexps around both on client and server side. + * + * In order to preserve the original JSON structure we need to convert the passed Regex patterns + * to a temporary string, then convert all to a legitimate JSON structure and then finally + * convert it back from string to a pattern. + * + * @param bodyAsValue - GString with passed values + * @param valueProvider - provider of values either for server or client side + * @return JSON structure with replaced client / server side parts + */ + public static Object extractValue(GString bodyAsValue, ContentType contentType, Closure valueProvider) { + if (contentType == ContentType.JSON) { + return extractValueForJSON(bodyAsValue, valueProvider) + } + if (contentType == ContentType.XML) { + return extractValueForXML(bodyAsValue, valueProvider) + } + // else Brute force :( + try { + return extractValueForJSON(bodyAsValue, valueProvider) + } catch(JsonException e) { + // Not a JSON format + return extractValueForXML(bodyAsValue, valueProvider) + } + return bodyAsValue + } + + public static Object extractValue(GString bodyAsValue, Closure valueProvider) { + return extractValue(bodyAsValue, ContentType.UNKNOWN, valueProvider) + } + + private static Object extractValueForJSON(GString bodyAsValue, Closure valueProvider) { + GString transformedString = new GStringImpl( + bodyAsValue.values.collect { transformJSONStringValue(it, valueProvider) } as String[], + bodyAsValue.strings.clone() as String[] + ) + def parsedJson = new JsonSlurper().parseText(transformedString.toString()) + return convertAllTemporaryRegexPlaceholdersBackToPatterns(parsedJson) + } + + private static GStringImpl extractValueForXML(GString bodyAsValue, Closure valueProvider) { + return new GStringImpl( + bodyAsValue.values.collect { transformXMLStringValue(it, valueProvider) } as String[], + bodyAsValue.strings.clone() as String[] + ) + } + + private static String transformJSONStringValue(Object obj, Closure valueProvider) { + return obj.toString() + } + + private static String transformJSONStringValue(DslProperty dslProperty, Closure valueProvider) { + return transformJSONStringValue(valueProvider(dslProperty), valueProvider) + } + + private static String transformJSONStringValue(Pattern pattern, Closure valueProvider) { + return String.format(JSON_VALUE_PATTERN_FOR_REGEX, pattern.pattern()) + } + + private static String transformXMLStringValue(Object obj, Closure valueProvider) { + return escapeXml11(obj.toString()) + } + + private static String transformXMLStringValue(DslProperty dslProperty, Closure valueProvider) { + return transformXMLStringValue(valueProvider(dslProperty), valueProvider) + } + + private static Object convertAllTemporaryRegexPlaceholdersBackToPatterns(parsedJson) { + JsonConverter.transformValues(parsedJson, { Object value -> + if (value instanceof String) { + String string = (String) value + Matcher matcher = TEMPORARY_PATTERN_HOLDER.matcher(string) + if (matcher.matches()) { + List val = matcher[0] as List + String pattern = val[1] + return Pattern.compile(pattern) + } + return value + } + return value + }) + } + + public static ContentType recognizeContentTypeFromHeader(Headers headers) { + String content = headers?.entries.find { it.name == "Content-Type" } ?.clientValue?.toString() + if (content?.endsWith("json")) { + return ContentType.JSON + } + if (content?.endsWith("xml")) { + return ContentType.XML + } + return ContentType.UNKNOWN + } + + public static MatchingStrategy.Type getEqualsTypeFromContentType(ContentType contentType) { + switch (contentType) { + case ContentType.JSON: + return MatchingStrategy.Type.EQUAL_TO_JSON + case ContentType.XML: + return MatchingStrategy.Type.EQUAL_TO_XML + } + return MatchingStrategy.Type.EQUAL_TO + } + + public static ContentType recognizeContentTypeFromContent(GString gstring) { + if (isJsonType(gstring)) { + return ContentType.JSON + } + if (isXmlType(gstring)) { + return ContentType.XML + } + return ContentType.UNKNOWN + } + + public static ContentType recognizeContentTypeFromContent(Map jsonMap) { + return ContentType.JSON + } + + public static ContentType recognizeContentTypeFromContent(List jsonList) { + return ContentType.JSON + } + + public static ContentType recognizeContentTypeFromContent(Object gstring) { + return ContentType.UNKNOWN + } + + public static boolean isJsonType(GString gstring) { + GString stringWithoutValues = new GStringImpl( + gstring.values.collect({ + it instanceof String || it instanceof GString ? it.toString() : escapeJson(it.toString()) + }) as Object[], + gstring.strings.clone() as String[] + ) + try { + new JsonSlurper().parseText(stringWithoutValues.toString()) + return true + } catch (JsonException e) { + // Not JSON + } + return false + } + + public static boolean isXmlType(GString gstring) { + GString stringWithoutValues = new GStringImpl( + gstring.values.collect({ + it instanceof String || it instanceof GString ? it.toString() : escapeXml11(it.toString()) + }) as Object[], + gstring.strings.clone() as String[] + ) + try { + new XmlSlurper().parseText(stringWithoutValues.toString()) + return true + } catch (Exception e) { + // Not XML + } + return false + } + + public static ContentType recognizeContentTypeFromMatchingStrategy(MatchingStrategy.Type type) { + switch (type) { + case MatchingStrategy.Type.EQUAL_TO_XML: + return ContentType.XML + case MatchingStrategy.Type.EQUAL_TO_JSON: + return ContentType.JSON + } + return ContentType.UNKNOWN + } + +} diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy index c8767551b4..5f6cb74fab 100755 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy @@ -181,7 +181,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { "urlPattern": "/[0-9]{2}", "bodyPatterns": [ { - "equalTo":"{\\"name\\":\\"Jan\\"}" + "equalToJson":"{\\"name\\":\\"Jan\\"}" } ] }, @@ -198,6 +198,314 @@ class WiremockGroovyDslSpec extends WiremockSpec { stubMappingIsValidWiremockStub(wiremockStub) } + def 'should use equalToJson when body match is defined as map'() { + given: + GroovyDsl groovyDsl = GroovyDsl.make { + request { + method('GET') + url $(client(~/\/[0-9]{2}/), server('/12')) + body( + id: value( + client('123'), + server({ regex('[0-9]+') }) + ), + surname: $( + client('Kowalsky'), + server('Lewandowski') + ), + name: 'Jan', + created: $(client('2014-02-02 12:23:43'), server({ currentDate(it) })) + ) + } + response { + status 200 + } + } + when: + String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockClientStub() + then: + new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText(''' + { + "request": { + "method": "GET", + "urlPattern": "/[0-9]{2}", + "bodyPatterns": [ + { + "equalToJson": "{\\"id\\":\\"123\\",\\"surname\\":\\"Kowalsky\\",\\"name\\":\\"Jan\\",\\"created\\":\\"2014-02-02 12:23:43\\"}" + } + ] + }, + "response": { + "status": 200, + } + } + ''') + and: + stubMappingIsValidWiremockStub(wiremockStub) + } + + def 'should use equalToJson when content type ends with json'() { + given: + GroovyDsl groovyDsl = GroovyDsl.make { + request { + method 'GET' + url "/users" + headers { + header "Content-Type", "customtype/json" + } + body """ + { + "name": "Jan" + } + """ + } + response { + status 200 + } + } + when: + String json = toWiremockClientJsonStub(groovyDsl) + then: + parseJson(json) == parseJson(''' + { + "request": { + "method": "GET", + "url": "/users", + "headers": { + "Content-Type": { + "equalTo": "customtype/json" + } + }, + "bodyPatterns": [ + { + "equalToJson":"{\\"name\\":\\"Jan\\"}" + } + ] + }, + "response": { + "status": 200 + } + } + ''') + and: + stubMappingIsValidWiremockStub(json) + } + + def 'should use equalToXml when content type ends with xml'() { + given: + GroovyDsl groovyDsl = GroovyDsl.make { + request { + method 'GET' + url "/users" + headers { + header "Content-Type", "customtype/xml" + } + body """${value(client('Jozo'), server('Denis'))}${value(client(""), server('1234567890'))}""" + } + response { + status 200 + } + } + when: + String json = toWiremockClientJsonStub(groovyDsl) + then: + parseJson(json) == parseJson(''' + { + "request": { + "method": "GET", + "url": "/users", + "headers": { + "Content-Type": { + "equalTo": "customtype/xml" + } + }, + "bodyPatterns": [ + { + "equalToXml":"Jozo<test>" + } + ] + }, + "response": { + "status": 200 + } + } + ''') + and: + stubMappingIsValidWiremockStub(json) + } + + def 'should use equalToXml when content type is parsable xml'() { + given: + GroovyDsl groovyDsl = GroovyDsl.make { + request { + method 'GET' + url "/users" + body """${value(client('Jozo'), server('Denis'))}${value(client(""), server('1234567890'))}""" + } + response { + status 200 + } + } + when: + String json = toWiremockClientJsonStub(groovyDsl) + then: + parseJson(json) == parseJson(''' + { + "request": { + "method": "GET", + "url": "/users", + "bodyPatterns": [ + { + "equalToXml":"Jozo<test>" + } + ] + }, + "response": { + "status": 200 + } + } + ''') + and: + stubMappingIsValidWiremockStub(json) + } + + def 'should support xml as a response body'() { + given: + GroovyDsl groovyDsl = GroovyDsl.make { + request { + method 'GET' + url "/users" + } + response { + status 200 + body """${value(client('Jozo'), server('Denis'))}${value(client(""), server('1234567890'))}""" + } + } + when: + String json = toWiremockClientJsonStub(groovyDsl) + then: + parseJson(json) == parseJson(''' + { + "request": { + "method": "GET", + "url": "/users" + }, + "response": { + "status": 200, + "body":"Jozo<test>" + } + } + ''') + and: + stubMappingIsValidWiremockStub(json) + } + + def 'should use equalToJson compare mode'() { + given: + GroovyDsl groovyDsl = GroovyDsl.make { + request { + method 'GET' + url "/users" + body equalToJsonWithStrictOrder('''{"name":"Jan"}''') + } + response { + status 200 + } + } + when: + String json = toWiremockClientJsonStub(groovyDsl) + then: + parseJson(json) == parseJson(''' + { + "request": { + "method": "GET", + "url": "/users", + "bodyPatterns": [ + { + "equalToJson":"{\\"name\\":\\"Jan\\"}", + "jsonCompareMode":"STRICT_ORDER" + } + ] + }, + "response": { + "status": 200 + } + } + ''') + and: + stubMappingIsValidWiremockStub(json) + } + + def 'should use equalToJson'() { + given: + GroovyDsl groovyDsl = GroovyDsl.make { + request { + method 'GET' + url "/users" + body equalToJson('''{"name":"Jan"}''') + } + response { + status 200 + } + } + when: + String json = toWiremockClientJsonStub(groovyDsl) + then: + parseJson(json) == parseJson(''' + { + "request": { + "method": "GET", + "url": "/users", + "bodyPatterns": [ + { + "equalToJson":"{\\"name\\":\\"Jan\\"}" + } + ] + }, + "response": { + "status": 200 + } + } + ''') + and: + stubMappingIsValidWiremockStub(json) + } + + def 'should use equalToXml'() { + given: + GroovyDsl groovyDsl = GroovyDsl.make { + request { + method 'GET' + url "/users" + body equalToXml("""${value(client('Jozo'), server('Denis'))}${value(client(""), server('1234567890'))}""") + } + response { + status 200 + } + } + when: + String json = toWiremockClientJsonStub(groovyDsl) + then: + parseJson(json) == parseJson(''' + { + "request": { + "method": "GET", + "url": "/users", + "bodyPatterns": [ + { + "equalToXml":"Jozo<test>" + } + ] + }, + "response": { + "status": 200 + } + } + ''') + and: + stubMappingIsValidWiremockStub(json) + } + def 'should convert groovy dsl stub with regexp Body as String to wiremock stub for the client side'() { given: GroovyDsl groovyDsl = GroovyDsl.make { @@ -232,9 +540,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { "method": "GET", "urlPattern": "/[0-9]{2}", "bodyPatterns": [ - { - "matches":"\\\\{\\"personalId\\":\\"^[0-9]{11}$\\"\\\\}" - } + {"matches": ".*personalId\\":.?\\"?^[0-9]{11}$\\"?.*"} ] }, "response": { @@ -294,9 +600,8 @@ class WiremockGroovyDslSpec extends WiremockSpec { }, "url": "/fraudcheck", "bodyPatterns": [ - { - "matches": "\\\\{\\"clientPesel\\":\\"[0-9]{10}\\",\\"loanAmount\\":123.123\\\\}" - } + {"matches": ".*clientPesel\\":.?\\"?[0-9]{10}\\"?.*"}, + {"matches": ".*loanAmount\\":.?\\"?123.123\\"?.*"} ] }, "response": { @@ -427,78 +732,78 @@ class WiremockGroovyDslSpec extends WiremockSpec { def "should generate request with urlPath for client side"() { given: - GroovyDsl groovyDsl = GroovyDsl.make { - request { - method 'GET' - urlPath $(client("boxes"), server("items")) - } - response { - status 200 - } - } - when: - def json = toWiremockClientJsonStub(groovyDsl) - then: - parseJson(json) == parseJson(''' - { - "request": { - "method": "GET", - "urlPath": "boxes" - }, - "response": { - "status": 200, + GroovyDsl groovyDsl = GroovyDsl.make { + request { + method 'GET' + urlPath $(client("boxes"), server("items")) + } + response { + status 200 } } - ''') + when: + def json = toWiremockClientJsonStub(groovyDsl) + then: + parseJson(json) == parseJson(''' + { + "request": { + "method": "GET", + "urlPath": "boxes" + }, + "response": { + "status": 200, + } + } + ''') and: - stubMappingIsValidWiremockStub(json) + stubMappingIsValidWiremockStub(json) } def "should generate simple request with urlPath for client side"() { given: - GroovyDsl groovyDsl = GroovyDsl.make { - request { - method 'GET' - urlPath "boxes" - } - response { - status 200 - } - } - when: - def json = toWiremockClientJsonStub(groovyDsl) - then: - parseJson(json) == parseJson(''' - { - "request": { - "method": "GET", - "urlPath": "boxes" - }, - "response": { - "status": 200, + GroovyDsl groovyDsl = GroovyDsl.make { + request { + method 'GET' + urlPath "boxes" + } + response { + status 200 } } - ''') + when: + def json = toWiremockClientJsonStub(groovyDsl) + then: + parseJson(json) == parseJson(''' + { + "request": { + "method": "GET", + "urlPath": "boxes" + }, + "response": { + "status": 200, + } + } + ''') and: - stubMappingIsValidWiremockStub(json) + stubMappingIsValidWiremockStub(json) } def "should not allow regexp in url for server value"() { when: - GroovyDsl.make { - request { - method 'GET' - url(regex(/users\/[0-9]*/)) { - queryParameters { - parameter 'age': notMatching("^\\w*\$") - parameter 'name': matching("Denis.*") + GroovyDsl.make { + request { + method 'GET' + url(regex(/users\/[0-9]*/)) { + queryParameters { + parameter 'age': notMatching("^\\w*\$") + parameter 'name': matching("Denis.*") + } } } + response { + status 200 + } } - response { - status 200 - } - } then: def e = thrown(IllegalStateException) e.message.contains "Url can't be a pattern for the server side" @@ -547,44 +852,44 @@ class WiremockGroovyDslSpec extends WiremockSpec { def "should generate request with url and queryParameters for client side"() { given: - GroovyDsl groovyDsl = GroovyDsl.make { - request { - method 'GET' - url($(client(regex(/users\/[0-9]*/)), server("users/123"))) { - queryParameters { - parameter 'age': $(client(notMatching("^\\w*\$")), server(10)) - parameter 'name': $(client(matching("Denis.*")), server("Denis")) + GroovyDsl groovyDsl = GroovyDsl.make { + request { + method 'GET' + url($(client(regex(/users\/[0-9]*/)), server("users/123"))) { + queryParameters { + parameter 'age': $(client(notMatching("^\\w*\$")), server(10)) + parameter 'name': $(client(matching("Denis.*")), server("Denis")) + } } } + response { + status 200 + } } - response { - status 200 - } - } when: - def json = toWiremockClientJsonStub(groovyDsl) + def json = toWiremockClientJsonStub(groovyDsl) then: - parseJson(json) == parseJson(''' - { - "request": { - "method": "GET", - "urlPattern": "users/[0-9]*", - "queryParameters": { - "age": { - "doesNotMatch": "^\\\\w*$" - }, - "name": { - "matches": "Denis.*" - } + parseJson(json) == parseJson(''' + { + "request": { + "method": "GET", + "urlPattern": "users/[0-9]*", + "queryParameters": { + "age": { + "doesNotMatch": "^\\\\w*$" + }, + "name": { + "matches": "Denis.*" + } + } + }, + "response": { + "status": 200, } - }, - "response": { - "status": 200, } - } - ''') + ''') and: - stubMappingIsValidWiremockStub(json) + stubMappingIsValidWiremockStub(json) } def "should generate stub with some headers section for client side"() { @@ -622,6 +927,74 @@ class WiremockGroovyDslSpec extends WiremockSpec { ''') } + def 'should convert groovy dsl stub with rich tree Body as String to wiremock stub for the client side'() { + given: + GroovyDsl groovyDsl = GroovyDsl.make { + request { + method('GET') + url $(client(~/\/[0-9]{2}/), server('/12')) + body """\ + { + "personalId": "${value(client(regex('[0-9]{11}')), server('57593728525'))}", + "firstName": "${value(client(regex('.*')), server('Bruce'))}", + "lastName": "${value(client(regex('.*')), server('Lee'))}", + "birthDate": "${value(client(regex('[0-9]{4}-[0-9]{2}-[0-9]{2}')), server('1985-12-12'))}", + "errors": [ + { + "propertyName": "${value(client(regex('[0-9]{2}')), server('04'))}", + "providerValue": "Test" + }, + { + "propertyName": "${value(client(regex('[0-9]{2}')), server('08'))}", + "providerValue": "Test" + } + ] + } + """ + } + response { + status 200 + body("""\ + { + "name": "Jan" + } + """ + ) + headers { + header 'Content-Type': 'text/plain' + } + } + } + when: + String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockClientStub() + then: + new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText(''' + { + "request": { + "method": "GET", + "urlPattern": "/[0-9]{2}", + "bodyPatterns": [ + {"matches": ".*birthDate\\":.?\\"?[0-9]{4}-[0-9]{2}-[0-9]{2}\\"?.*"}, + {"matches": ".*propertyName\\":.?\\"?[0-9]{2}\\"?.*"}, + {"matches": ".*providerValue\\":.?\\"?Test\\"?.*"}, + {"matches": ".*propertyName\\":.?\\"?[0-9]{2}\\"?.*"}, + {"matches": ".*providerValue\\":.?\\"?Test\\"?.*"}, + {"matches": ".*firstName\\":.?\\"?.*\\"?.*"}, + {"matches": ".*lastName\\":.?\\"?.*\\"?.*"}, + {"matches": ".*personalId\\":.?\\"?[0-9]{11}\\"?.*"} + ] + }, + "response": { + "status": 200, + "body": "{\\"name\\":\\"Jan\\"}", + "headers": { + "Content-Type": "text/plain" + } + } + } + ''') + } + String toJsonString(value) { new JsonBuilder(value).toPrettyString() } diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockSpec.groovy index 796e603c11..ab1133c9ae 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockSpec.groovy @@ -5,7 +5,7 @@ import spock.lang.Specification import java.util.regex.Pattern -class WiremockSpec extends Specification { +abstract class WiremockSpec extends Specification { void stubMappingIsValidWiremockStub(String mappingDefinition) { StubMapping stubMapping = StubMapping.buildFrom(mappingDefinition) diff --git a/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/BasicFunctionalSpec.groovy b/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/BasicFunctionalSpec.groovy index e2ee36959d..96cabca538 100755 --- a/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/BasicFunctionalSpec.groovy +++ b/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/BasicFunctionalSpec.groovy @@ -52,7 +52,7 @@ class BasicFunctionalSpec extends IntegrationSpec { }, "url": "/api/12", "bodyPatterns": [ - { "equalTo": "[{\\"text\\":\\"Gonna see you at Warsaw\\"}]" } + { "equalToJson": "[{\\"text\\":\\"Gonna see you at Warsaw\\"}]" } ] }, "response": { From 69e5020e3514dee6ee3a32ba79f5e20d8894c1f2 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Mon, 15 Jun 2015 16:45:52 +0200 Subject: [PATCH 035/119] [#82] Fixed wrong request creation when having list in a map --- .../builder/SpockMethodBodyBuilder.groovy | 15 ++++++++--- .../accurest/util/ContentUtils.groovy | 13 ++++++++-- .../builder/SpockMethodBuilderSpec.groovy | 26 +++++++++++++++++-- 3 files changed, 46 insertions(+), 8 deletions(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy index 544abb98b1..d569204b10 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy @@ -12,6 +12,7 @@ import io.codearte.accurest.dsl.internal.Request import io.codearte.accurest.dsl.internal.Response import io.codearte.accurest.dsl.internal.UrlPath import io.codearte.accurest.util.ContentType +import io.codearte.accurest.util.JsonConverter import java.util.regex.Pattern @@ -42,10 +43,7 @@ class SpockMethodBodyBuilder { addLine(".header('${header.name}', '${header.serverValue}')") } if (request.body) { - Object bodyValue = request.body.serverValue - if (bodyValue instanceof GString) { - bodyValue = extractValue(bodyValue, {DslProperty dslProperty -> dslProperty.serverValue}) - } + Object bodyValue = extractServerValueFromBody(request.body.serverValue) String matches = new JsonOutput().toJson(bodyValue) addLine(".body('$matches')") } @@ -97,6 +95,15 @@ class SpockMethodBodyBuilder { } } + private Object extractServerValueFromBody(bodyValue) { + if (bodyValue instanceof GString) { + bodyValue = extractValue(bodyValue, { DslProperty dslProperty -> dslProperty.serverValue }) + } else { + bodyValue = JsonConverter.transformValues(bodyValue, { it instanceof DslProperty ? it.serverValue : it }) + } + return bodyValue + } + private String buildUrl(Request request) { if (request.url) return request.url.serverValue; diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy index 1a187595b9..6cab8d76fd 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy @@ -2,6 +2,7 @@ package io.codearte.accurest.util import groovy.json.JsonException import groovy.json.JsonSlurper import groovy.transform.TypeChecked +import groovy.util.logging.Slf4j import io.codearte.accurest.dsl.internal.DslProperty import io.codearte.accurest.dsl.internal.Headers import io.codearte.accurest.dsl.internal.MatchingStrategy @@ -14,6 +15,7 @@ import static org.apache.commons.lang3.StringEscapeUtils.escapeJson import static org.apache.commons.lang3.StringEscapeUtils.escapeXml11 @TypeChecked +@Slf4j class ContentUtils { private static final Pattern TEMPORARY_PATTERN_HOLDER = Pattern.compile('REGEXP>>(.*)<<') @@ -40,12 +42,19 @@ class ContentUtils { } // else Brute force :( try { + log.debug("No content type provided so trying to parse as JSON") return extractValueForJSON(bodyAsValue, valueProvider) } catch(JsonException e) { // Not a JSON format - return extractValueForXML(bodyAsValue, valueProvider) + log.debug("Failed to parse as JSON - trying to parse as XML", e) + try { + return extractValueForXML(bodyAsValue, valueProvider) + } catch (Exception exception) { + log.debug("No content type provided and failed to parse as XML - returning the value back to the user", exception) + return bodyAsValue + } } - return bodyAsValue + } public static Object extractValue(GString bodyAsValue, Closure valueProvider) { diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy index a2d03358a2..d8bfefcf5e 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy @@ -34,7 +34,7 @@ class SpockMethodBuilderSpec extends Specification { } @Issue("#79") - def "should generate assertions for simple response body constructed from map"() { + def "should generate assertions for simple response body constructed from map with a list"() { given: GroovyDsl contractDsl = GroovyDsl.make { request { @@ -62,6 +62,29 @@ class SpockMethodBuilderSpec extends Specification { blockBuilder.toString().contains("responseBody.property2[1].b == \"sthElse\"") } + @Issue("#82") + def "should generate proper request when body constructed from map with a list"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "GET" + url "test" + body ( + items: ['HOP'] + ) + } + response { + status 200 + } + } + SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains(".body('{\"items\":[\"HOP\"]}')") + } + def "should generate assertions for array in response body"() { given: GroovyDsl contractDsl = GroovyDsl.make { @@ -236,5 +259,4 @@ class SpockMethodBuilderSpec extends Specification { spockTest.contains('responseBody.property1 == "a"') spockTest.contains('responseBody.property2 == "b"') } - } From 67f9e76d7df8c5f08a739bcf769c41cf4e6167cb Mon Sep 17 00:00:00 2001 From: Denis Stepanov Date: Mon, 15 Jun 2015 17:36:19 +0200 Subject: [PATCH 036/119] Remove Json matching --- .../accurest/dsl/internal/Request.groovy | 20 ----------- .../accurest/dsl/WiremockGroovyDslSpec.groovy | 36 ------------------- 2 files changed, 56 deletions(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Request.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Request.groovy index 67b9dde09f..1ed604d4fb 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Request.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Request.groovy @@ -125,26 +125,6 @@ class Request extends Common { return new MatchingStrategy(value, MatchingStrategy.Type.EQUAL_TO_JSON) } - MatchingStrategy equalToJson(Object value, JSONCompareMode jsonCompareMode) { - return new MatchingStrategy(value, MatchingStrategy.Type.EQUAL_TO_JSON) - } - - MatchingStrategy equalToJsonStrictly(Object value) { - return new MatchingStrategy(value, MatchingStrategy.Type.EQUAL_TO_JSON, JSONCompareMode.STRICT) - } - - MatchingStrategy equalToJsonLeniently(Object value) { - return new MatchingStrategy(value, MatchingStrategy.Type.EQUAL_TO_JSON, JSONCompareMode.LENIENT) - } - - MatchingStrategy equalToJsonNonExtensibly(Object value) { - return new MatchingStrategy(value, MatchingStrategy.Type.EQUAL_TO_JSON, JSONCompareMode.NON_EXTENSIBLE) - } - - MatchingStrategy equalToJsonWithStrictOrder(Object value) { - return new MatchingStrategy(value, MatchingStrategy.Type.EQUAL_TO_JSON, JSONCompareMode.STRICT_ORDER) - } - } @CompileStatic diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy index 5f6cb74fab..c066f4a9cc 100755 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy @@ -400,42 +400,6 @@ class WiremockGroovyDslSpec extends WiremockSpec { stubMappingIsValidWiremockStub(json) } - def 'should use equalToJson compare mode'() { - given: - GroovyDsl groovyDsl = GroovyDsl.make { - request { - method 'GET' - url "/users" - body equalToJsonWithStrictOrder('''{"name":"Jan"}''') - } - response { - status 200 - } - } - when: - String json = toWiremockClientJsonStub(groovyDsl) - then: - parseJson(json) == parseJson(''' - { - "request": { - "method": "GET", - "url": "/users", - "bodyPatterns": [ - { - "equalToJson":"{\\"name\\":\\"Jan\\"}", - "jsonCompareMode":"STRICT_ORDER" - } - ] - }, - "response": { - "status": 200 - } - } - ''') - and: - stubMappingIsValidWiremockStub(json) - } - def 'should use equalToJson'() { given: GroovyDsl groovyDsl = GroovyDsl.make { From 920d6fa1e6d7063d2192e65a878134eddaef658d Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Tue, 16 Jun 2015 14:08:58 +0200 Subject: [PATCH 037/119] Raised the version of gradle-nexus-staging-plugin to 0.5.3. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 8000423e6c..2ad0eb7a0c 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ buildscript { } dependencies { classpath "pl.allegro.tech.build:axion-release-plugin:1.2.2" - classpath "io.codearte.gradle.nexus:gradle-nexus-staging-plugin:0.5.1" + classpath "io.codearte.gradle.nexus:gradle-nexus-staging-plugin:0.5.3" } } From ce2d4093d03dff399bef4cafa0d810791b7bce2a Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Tue, 16 Jun 2015 18:51:03 +0200 Subject: [PATCH 038/119] [#86] Fixed issue with Gstring, wiremock and regexp --- .../dsl/WiremockRequestStubStrategy.groovy | 16 +++--- .../accurest/dsl/WiremockGroovyDslSpec.groovy | 49 +++++++++++++++++++ 2 files changed, 59 insertions(+), 6 deletions(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy index f9bc9bdc23..ea0b6b4a9f 100755 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy @@ -131,13 +131,17 @@ class WiremockRequestStubStrategy extends BaseWiremockStubStrategy { private List extractReqexpMatching(Object responseBodyObject) { def matchingStrategies = new ArrayList() - responseBodyObject.each { k, v -> - if (v instanceof List) { - v.each { - matchingStrategies.addAll(extractReqexpMatching((Map)it)) + if (responseBodyObject instanceof GString) { + return [new MatchingStrategy(responseBodyObject, MatchingStrategy.Type.MATCHING)] + } else if (responseBodyObject instanceof Map) { + responseBodyObject.each { k, v -> + if (v instanceof List) { + v.each { + matchingStrategies.addAll(extractReqexpMatching((Map)it)) + } + } else { + matchingStrategies.add(new MatchingStrategy(/.*${k}":.?"?${v}"?.*/, MatchingStrategy.Type.MATCHING)) } - } else { - matchingStrategies.add(new MatchingStrategy(/.*${k}":.?"?${v}"?.*/, MatchingStrategy.Type.MATCHING)) } } return matchingStrategies diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy index c066f4a9cc..80540ec70d 100755 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy @@ -101,6 +101,55 @@ class WiremockGroovyDslSpec extends WiremockSpec { stubMappingIsValidWiremockStub(wiremockStub) } + @Issue("#86") + def 'should convert groovy dsl stub with GString and regexp'() { + given: + GroovyDsl groovyDsl = GroovyDsl.make { + request { + method('POST') + url('/ws/payments') + headers { + header("Content-Type": 'application/x-www-form-urlencoded') + } + body("""paymentType=INCOMING&transferType=BANK&amount=${value(client(regex('[0-9]{3}\\.[0-9]{2}')), server(500.00))}&bookingDate=${value(client(regex('[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])')), server('2015-05-18'))}""") + } + response { + status 204 + body( + paymentId: value(client('4'), server(regex('[1-9][0-9]*'))), + foundExistingPayment: false + ) + } + } + when: + String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockClientStub() + then: + new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText(''' +{ + "request": { + "method": "POST", + "headers": { + "Content-Type": { + "equalTo": "application/x-www-form-urlencoded" + } + }, + "url": "/ws/payments", + "bodyPatterns": [ + { + "matches": "paymentType=INCOMING&transferType=BANK&amount=[0-9]{3}\\\\.[0-9]{2}&bookingDate=[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])" + } + ] + }, + "response": { + "status": 204, + "body": "{\\"paymentId\\":\\"4\\",\\"foundExistingPayment\\":false}" + } +} +''') + and: + stubMappingIsValidWiremockStub(wiremockStub) + } + def 'should convert groovy dsl stub with Body as String to wiremock stub for the client side'() { given: GroovyDsl groovyDsl = GroovyDsl.make { From 5e83337ad34bc5f0edc4ca1e04b5791a003ed5a4 Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Wed, 17 Jun 2015 11:55:44 +0200 Subject: [PATCH 039/119] Release version: 0.6.7 [ci skip] From 3755f530311a414df718451c52f87f505a0400b6 Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Thu, 18 Jun 2015 12:53:28 +0200 Subject: [PATCH 040/119] Caused by JsonOutput().toJson() Added trimming of additional quotes. --- .../builder/SpockMethodBodyBuilder.groovy | 280 ++++++----- .../builder/SpockMethodBuilderSpec.groovy | 453 +++++++++--------- 2 files changed, 378 insertions(+), 355 deletions(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy index d569204b10..d42350386b 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy @@ -3,175 +3,173 @@ package io.codearte.accurest.builder import groovy.json.JsonOutput import groovy.transform.PackageScope import io.codearte.accurest.dsl.GroovyDsl -import io.codearte.accurest.dsl.internal.DslProperty -import io.codearte.accurest.dsl.internal.ExecutionProperty -import io.codearte.accurest.dsl.internal.Header -import io.codearte.accurest.dsl.internal.MatchingStrategy -import io.codearte.accurest.dsl.internal.QueryParameter -import io.codearte.accurest.dsl.internal.Request -import io.codearte.accurest.dsl.internal.Response -import io.codearte.accurest.dsl.internal.UrlPath +import io.codearte.accurest.dsl.internal.* import io.codearte.accurest.util.ContentType import io.codearte.accurest.util.JsonConverter import java.util.regex.Pattern -import static io.codearte.accurest.util.ContentUtils.extractValue -import static io.codearte.accurest.util.ContentUtils.recognizeContentTypeFromHeader -import static io.codearte.accurest.util.ContentUtils.recognizeContentTypeFromContent +import static io.codearte.accurest.util.ContentUtils.* /** * @author Jakub Kubrynski */ @PackageScope class SpockMethodBodyBuilder { - private final GroovyDsl stubDefinition + private final GroovyDsl stubDefinition - SpockMethodBodyBuilder(GroovyDsl stubDefinition) { - this.stubDefinition = stubDefinition - } + SpockMethodBodyBuilder(GroovyDsl stubDefinition) { + this.stubDefinition = stubDefinition + } - void appendTo(BlockBuilder blockBuilder) { - Request request = stubDefinition.request - Response response = stubDefinition.response - blockBuilder.with { - startBlock() - addLine('given:').startBlock() - addLine('def request = given()') - indent() - request.headers?.collect { Header header -> - addLine(".header('${header.name}', '${header.serverValue}')") - } - if (request.body) { - Object bodyValue = extractServerValueFromBody(request.body.serverValue) - String matches = new JsonOutput().toJson(bodyValue) - addLine(".body('$matches')") - } + void appendTo(BlockBuilder blockBuilder) { + Request request = stubDefinition.request + Response response = stubDefinition.response + blockBuilder.with { + startBlock() + addLine('given:').startBlock() + addLine('def request = given()') + indent() + request.headers?.collect { Header header -> + addLine(".header('${header.name}', '${header.serverValue}')") + } + if (request.body) { + Object bodyValue = extractServerValueFromBody(request.body.serverValue) + String matches = trimRepeatedQuotes(new JsonOutput().toJson(bodyValue)) + addLine(".body('$matches')") + } - unindent().endBlock().addEmptyLine() + unindent().endBlock().addEmptyLine() - addLine('when:').startBlock() - addLine('def response = given().spec(request)') - indent() + addLine('when:').startBlock() + addLine('def response = given().spec(request)') + indent() - String url = buildUrl(request) - String method = request.method.serverValue.toLowerCase() + String url = buildUrl(request) + String method = request.method.serverValue.toLowerCase() - blockBuilder.addLine(/.${method}("$url")/) - unindent().endBlock().addEmptyLine() + blockBuilder.addLine(/.${method}("$url")/) + unindent().endBlock().addEmptyLine() - addLine('then:').startBlock() - addLine("response.statusCode == $response.status.serverValue") + addLine('then:').startBlock() + addLine("response.statusCode == $response.status.serverValue") - response.headers?.collect { Header header -> - addLine("response.header('$header.name') == '$header.serverValue'") - } - if (response.body) { - endBlock() - addLine('and:').startBlock() - def responseBody = response.body.serverValue - ContentType contentType = recognizeContentTypeFromHeader(response.headers) - if (contentType == ContentType.UNKNOWN) { - contentType = recognizeContentTypeFromContent(responseBody) - } - if (responseBody instanceof GString) { - responseBody = extractValue(responseBody, contentType, { DslProperty dslProperty -> dslProperty.serverValue }) - } - if (contentType == ContentType.JSON) { - addLine('def responseBody = new JsonSlurper().parseText(response.body.asString())') - if (responseBody instanceof List) { - processArrayElements(responseBody, "", blockBuilder) - } else { - processMapElement(responseBody, blockBuilder, "") - } - } else if (contentType == ContentType.XML) { - addLine('def responseBody = new XmlSlurper().parseText(response.body.asString())') - // TODO xml validation - } - } - endBlock() + response.headers?.collect { Header header -> + addLine("response.header('$header.name') == '$header.serverValue'") + } + if (response.body) { + endBlock() + addLine('and:').startBlock() + def responseBody = response.body.serverValue + ContentType contentType = recognizeContentTypeFromHeader(response.headers) + if (contentType == ContentType.UNKNOWN) { + contentType = recognizeContentTypeFromContent(responseBody) + } + if (responseBody instanceof GString) { + responseBody = extractValue(responseBody, contentType, { DslProperty dslProperty -> dslProperty.serverValue }) + } + if (contentType == ContentType.JSON) { + addLine('def responseBody = new JsonSlurper().parseText(response.body.asString())') + if (responseBody instanceof List) { + processArrayElements(responseBody, "", blockBuilder) + } else { + processMapElement(responseBody, blockBuilder, "") + } + } else if (contentType == ContentType.XML) { + addLine('def responseBody = new XmlSlurper().parseText(response.body.asString())') + // TODO xml validation + } + } + endBlock() - endBlock() - } - } + endBlock() + } + } - private Object extractServerValueFromBody(bodyValue) { - if (bodyValue instanceof GString) { - bodyValue = extractValue(bodyValue, { DslProperty dslProperty -> dslProperty.serverValue }) - } else { - bodyValue = JsonConverter.transformValues(bodyValue, { it instanceof DslProperty ? it.serverValue : it }) - } - return bodyValue - } + static String trimRepeatedQuotes(String toTrim) { + if (toTrim.startsWith('"')) { + return toTrim.replaceAll('"', '') + } + return toTrim + } - private String buildUrl(Request request) { - if (request.url) - return request.url.serverValue; - if (request.urlPath) - return buildUrlFromUrlPath(request.urlPath) - throw new IllegalStateException("URL is not set!") - } + private Object extractServerValueFromBody(bodyValue) { + if (bodyValue instanceof GString) { + bodyValue = extractValue(bodyValue, { DslProperty dslProperty -> dslProperty.serverValue }) + } else { + bodyValue = JsonConverter.transformValues(bodyValue, { it instanceof DslProperty ? it.serverValue : it }) + } + return bodyValue + } - private String buildUrlFromUrlPath(UrlPath urlPath) { - String params = urlPath.queryParameters.parameters.inject([]) { result, param -> - result << "${param.name}=${URLEncoder.encode(resolveParamValue(param).toString(), "UTF8")}" - }.join('&') - return "$urlPath.serverValue?$params" - } + private String buildUrl(Request request) { + if (request.url) + return request.url.serverValue; + if (request.urlPath) + return buildUrlFromUrlPath(request.urlPath) + throw new IllegalStateException("URL is not set!") + } - private String resolveParamValue(QueryParameter param) { - resolveParamValue(param.serverValue) - } + private String buildUrlFromUrlPath(UrlPath urlPath) { + String params = urlPath.queryParameters.parameters.inject([]) { result, param -> + result << "${param.name}=${URLEncoder.encode(resolveParamValue(param).toString(), "UTF8")}" + }.join('&') + return "$urlPath.serverValue?$params" + } - private String resolveParamValue(Object value) { - value.toString() - } + private String resolveParamValue(QueryParameter param) { + resolveParamValue(param.serverValue) + } - private String resolveParamValue(MatchingStrategy matchingStrategy) { - matchingStrategy.serverValue.toString() - } + private String resolveParamValue(Object value) { + value.toString() + } - private void processBodyElement(BlockBuilder blockBuilder, String property, def value) { - if (value instanceof String) { - if (value.startsWith('$')) { - value = value.substring(1).replaceAll('\\$value', "responseBody$property") - blockBuilder.addLine(value) - } else { - blockBuilder.addLine("responseBody$property == \"${value}\"") - } - } else if (value instanceof Map) { - processMapElement(value, blockBuilder, property) - }else if (value instanceof Map.Entry) { - processEntryElement(blockBuilder, property, value) - } else if (value instanceof List) { - processArrayElements(value, property, blockBuilder) - } else if (value instanceof Pattern) { - blockBuilder.addLine("responseBody$property ==~ java.util.regex.Pattern.compile('${value}')") - } else if (value instanceof DslProperty) { - processBodyElement(blockBuilder, property, value.serverValue) - } else if (value instanceof ExecutionProperty) { - ExecutionProperty exec = (ExecutionProperty) value - blockBuilder.addLine("${exec.insertValue("responseBody$property")}") - } else { - blockBuilder.addLine("responseBody$property == ${value}") - } - } + private String resolveParamValue(MatchingStrategy matchingStrategy) { + matchingStrategy.serverValue.toString() + } - private void processMapElement(def value, BlockBuilder blockBuilder, String property) { - value.each { entry -> processEntryElement(blockBuilder, property, entry) } - } + private void processBodyElement(BlockBuilder blockBuilder, String property, def value) { + if (value instanceof String) { + if (value.startsWith('$')) { + value = value.substring(1).replaceAll('\\$value', "responseBody$property") + blockBuilder.addLine(value) + } else { + blockBuilder.addLine("responseBody$property == \"${value}\"") + } + } else if (value instanceof Map) { + processMapElement(value, blockBuilder, property) + } else if (value instanceof Map.Entry) { + processEntryElement(blockBuilder, property, value) + } else if (value instanceof List) { + processArrayElements(value, property, blockBuilder) + } else if (value instanceof Pattern) { + blockBuilder.addLine("responseBody$property ==~ java.util.regex.Pattern.compile('${value}')") + } else if (value instanceof DslProperty) { + processBodyElement(blockBuilder, property, value.serverValue) + } else if (value instanceof ExecutionProperty) { + ExecutionProperty exec = (ExecutionProperty) value + blockBuilder.addLine("${exec.insertValue("responseBody$property")}") + } else { + blockBuilder.addLine("responseBody$property == ${value}") + } + } - private def processEntryElement(BlockBuilder blockBuilder, String property, def entry) { - return processBodyElement(blockBuilder, property + "." + entry.key, entry.value) - } + private void processMapElement(def value, BlockBuilder blockBuilder, String property) { + value.each { entry -> processEntryElement(blockBuilder, property, entry) } + } - private void processArrayElements(List responseBody, String property, BlockBuilder blockBuilder) { - responseBody.eachWithIndex { - listElement, listIndex -> - listElement.each { entry -> - String prop = "$property[$listIndex]" ?: '' - processBodyElement(blockBuilder, prop, entry) - } - } - } + private def processEntryElement(BlockBuilder blockBuilder, String property, def entry) { + return processBodyElement(blockBuilder, property + "." + entry.key, entry.value) + } + + private void processArrayElements(List responseBody, String property, BlockBuilder blockBuilder) { + responseBody.eachWithIndex { + listElement, listIndex -> + listElement.each { entry -> + String prop = "$property[$listIndex]" ?: '' + processBodyElement(blockBuilder, prop, entry) + } + } + } } diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy index d8bfefcf5e..35135dff7b 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy @@ -9,254 +9,279 @@ import spock.lang.Specification */ class SpockMethodBuilderSpec extends Specification { - def "should generate assertions for simple response body"() { - given: - GroovyDsl contractDsl = GroovyDsl.make { - request { - method "GET" - url "test" - } - response { - status 200 - body """{ + def "should generate assertions for simple response body"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "GET" + url "test" + } + response { + status 200 + body """{ "property1": "a", "property2": "b" }""" - } - } - SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) - BlockBuilder blockBuilder = new BlockBuilder(" ") - when: - builder.appendTo(blockBuilder) - then: - blockBuilder.toString().contains("responseBody.property1 == \"a\"") - blockBuilder.toString().contains("responseBody.property2 == \"b\"") - } + } + } + SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains("responseBody.property1 == \"a\"") + blockBuilder.toString().contains("responseBody.property2 == \"b\"") + } - @Issue("#79") - def "should generate assertions for simple response body constructed from map with a list"() { - given: - GroovyDsl contractDsl = GroovyDsl.make { - request { - method "GET" - url "test" - } - response { - status 200 - body ( - property1: 'a', - property2: [ - [a: 'sth'], - [b: 'sthElse'] - ] - ) - } - } - SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) - BlockBuilder blockBuilder = new BlockBuilder(" ") - when: - builder.appendTo(blockBuilder) - then: - blockBuilder.toString().contains("responseBody.property1 == \"a\"") - blockBuilder.toString().contains("responseBody.property2[0].a == \"sth\"") - blockBuilder.toString().contains("responseBody.property2[1].b == \"sthElse\"") - } + @Issue("#79") + def "should generate assertions for simple response body constructed from map with a list"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "GET" + url "test" + } + response { + status 200 + body( + property1: 'a', + property2: [ + [a: 'sth'], + [b: 'sthElse'] + ] + ) + } + } + SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains("responseBody.property1 == \"a\"") + blockBuilder.toString().contains("responseBody.property2[0].a == \"sth\"") + blockBuilder.toString().contains("responseBody.property2[1].b == \"sthElse\"") + } - @Issue("#82") - def "should generate proper request when body constructed from map with a list"() { - given: - GroovyDsl contractDsl = GroovyDsl.make { - request { - method "GET" - url "test" - body ( - items: ['HOP'] - ) - } - response { - status 200 - } - } - SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) - BlockBuilder blockBuilder = new BlockBuilder(" ") - when: - builder.appendTo(blockBuilder) - then: - blockBuilder.toString().contains(".body('{\"items\":[\"HOP\"]}')") - } + @Issue("#82") + def "should generate proper request when body constructed from map with a list"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "GET" + url "test" + body( + items: ['HOP'] + ) + } + response { + status 200 + } + } + SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains(".body('{\"items\":[\"HOP\"]}')") + } - def "should generate assertions for array in response body"() { - given: - GroovyDsl contractDsl = GroovyDsl.make { - request { - method "GET" - url "test" - } - response { - status 200 - body """[ + @Issue("#88") + def "should generate proper request when body constructed from GString"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "GET" + url "test" + body( + "property1=VAL1" + ) + } + response { + status 200 + } + } + SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains(".body('property1=VAL1')") + } + + def "should generate assertions for array in response body"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "GET" + url "test" + } + response { + status 200 + body """[ { "property1": "a" }, { "property2": "b" }]""" - } - } - SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) - BlockBuilder blockBuilder = new BlockBuilder(" ") - when: - builder.appendTo(blockBuilder) - then: - blockBuilder.toString().contains("responseBody[0].property1 == \"a\"") - blockBuilder.toString().contains("responseBody[1].property2 == \"b\"") - } + } + } + SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains("responseBody[0].property1 == \"a\"") + blockBuilder.toString().contains("responseBody[1].property2 == \"b\"") + } - def "should generate assertions for array inside response body element"() { - given: - GroovyDsl contractDsl = GroovyDsl.make { - request { - method "GET" - url "test" - } - response { - status 200 - body """{ + def "should generate assertions for array inside response body element"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "GET" + url "test" + } + response { + status 200 + body """{ "property1": [ { "property2": "test1"}, { "property3": "test2"} ] }""" - } - } - SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) - BlockBuilder blockBuilder = new BlockBuilder(" ") - when: - builder.appendTo(blockBuilder) - then: - blockBuilder.toString().contains("responseBody.property1[0].property2 == \"test1\"") - blockBuilder.toString().contains("responseBody.property1[1].property3 == \"test2\"") - } + } + } + SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains("responseBody.property1[0].property2 == \"test1\"") + blockBuilder.toString().contains("responseBody.property1[1].property3 == \"test2\"") + } - def "should generate assertions for nested objects in response body"() { - given: - GroovyDsl contractDsl = GroovyDsl.make { - request { - method "GET" - url "test" - } - response { - status 200 - body '''\ + def "should generate assertions for nested objects in response body"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "GET" + url "test" + } + response { + status 200 + body '''\ { "property1": "a", "property2": {"property3": "b"} } ''' - } - } - SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) - BlockBuilder blockBuilder = new BlockBuilder(" ") - when: - builder.appendTo(blockBuilder) - then: - blockBuilder.toString().contains("responseBody.property1 == \"a\"") - blockBuilder.toString().contains("responseBody.property2.property3 == \"b\"") - } + } + } + SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains("responseBody.property1 == \"a\"") + blockBuilder.toString().contains("responseBody.property2.property3 == \"b\"") + } - def "should generate regex assertions for map objects in response body"() { - given: - GroovyDsl contractDsl = GroovyDsl.make { - request { - method "GET" - url "test" - } - response { - status 200 - body( - property1: "a", - property2: value( - client('123'), - server(regex('[0-9]{3}')) - ) - ) - headers { - header('Content-Type': 'application/json') + def "should generate regex assertions for map objects in response body"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "GET" + url "test" + } + response { + status 200 + body( + property1: "a", + property2: value( + client('123'), + server(regex('[0-9]{3}')) + ) + ) + headers { + header('Content-Type': 'application/json') - } + } - } - } - SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) - BlockBuilder blockBuilder = new BlockBuilder(" ") - when: - builder.appendTo(blockBuilder) - then: - blockBuilder.toString().contains("responseBody.property1 == \"a\"") - blockBuilder.toString().contains("responseBody.property2 ==~ java.util.regex.Pattern.compile('[0-9]{3}')") - } + } + } + SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains("responseBody.property1 == \"a\"") + blockBuilder.toString().contains("responseBody.property2 ==~ java.util.regex.Pattern.compile('[0-9]{3}')") + } - def "should generate regex assertions for string objects in response body"() { - given: - GroovyDsl contractDsl = GroovyDsl.make { - request { - method "GET" - url "test" - } - response { - status 200 - body( """{"property1":"a","property2":"${value(client('123'), server(regex('[0-9]{3}')))}"}""") - headers { - header('Content-Type': 'application/json') + def "should generate regex assertions for string objects in response body"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "GET" + url "test" + } + response { + status 200 + body("""{"property1":"a","property2":"${value(client('123'), server(regex('[0-9]{3}')))}"}""") + headers { + header('Content-Type': 'application/json') - } + } - } - } - SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) - BlockBuilder blockBuilder = new BlockBuilder(" ") - when: - builder.appendTo(blockBuilder) - then: - blockBuilder.toString().contains("responseBody.property1 == \"a\"") - blockBuilder.toString().contains("responseBody.property2 ==~ java.util.regex.Pattern.compile('[0-9]{3}')") - } + } + } + SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains("responseBody.property1 == \"a\"") + blockBuilder.toString().contains("responseBody.property2 ==~ java.util.regex.Pattern.compile('[0-9]{3}')") + } - def "should generate a call with an url path and query parameters"() { - given: - GroovyDsl contractDsl = GroovyDsl.make { - request { - method 'GET' - urlPath('/users') { - queryParameters { - parameter 'limit': $(client(equalTo("20")), server(equalTo("10"))) - parameter 'offset': $(client(containing("20")), server(equalTo("20"))) - parameter 'filter': "email" - parameter 'sort': equalTo("name") - parameter 'search': $(client(notMatching(~/^\/[0-9]{2}$/)), server("55")) - parameter 'age': $(client(notMatching("^\\w*\$")), server("99")) - parameter 'name': $(client(matching("Denis.*")), server("Denis.Stepanov")) - } - } - } - response { - status 200 - body """ + def "should generate a call with an url path and query parameters"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method 'GET' + urlPath('/users') { + queryParameters { + parameter 'limit': $(client(equalTo("20")), server(equalTo("10"))) + parameter 'offset': $(client(containing("20")), server(equalTo("20"))) + parameter 'filter': "email" + parameter 'sort': equalTo("name") + parameter 'search': $(client(notMatching(~/^\/[0-9]{2}$/)), server("55")) + parameter 'age': $(client(notMatching("^\\w*\$")), server("99")) + parameter 'name': $(client(matching("Denis.*")), server("Denis.Stepanov")) + } + } + } + response { + status 200 + body """ { "property1": "a", "property2": "b" } """ - } - } - SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) - BlockBuilder blockBuilder = new BlockBuilder(" ") - when: - builder.appendTo(blockBuilder) - def spockTest = blockBuilder.toString() - then: - spockTest.contains('get("/users?limit=10&offset=20&filter=email&sort=name&search=55&age=99&name=Denis.Stepanov")') - spockTest.contains('responseBody.property1 == "a"') - spockTest.contains('responseBody.property2 == "b"') - } + } + } + SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + def spockTest = blockBuilder.toString() + then: + spockTest.contains('get("/users?limit=10&offset=20&filter=email&sort=name&search=55&age=99&name=Denis.Stepanov")') + spockTest.contains('responseBody.property1 == "a"') + spockTest.contains('responseBody.property2 == "b"') + } + + } From 84520dafa5f79e2cbc3a1eb77024d9bdec7a4477 Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Thu, 18 Jun 2015 12:56:10 +0200 Subject: [PATCH 041/119] Changed code formatting in modified classes to Codearte style. --- .../builder/SpockMethodBodyBuilder.groovy | 285 ++++++----- .../builder/SpockMethodBuilderSpec.groovy | 472 +++++++++--------- 2 files changed, 383 insertions(+), 374 deletions(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy index d42350386b..e3c0b1388b 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy @@ -3,173 +3,182 @@ package io.codearte.accurest.builder import groovy.json.JsonOutput import groovy.transform.PackageScope import io.codearte.accurest.dsl.GroovyDsl -import io.codearte.accurest.dsl.internal.* +import io.codearte.accurest.dsl.internal.DslProperty +import io.codearte.accurest.dsl.internal.ExecutionProperty +import io.codearte.accurest.dsl.internal.Header +import io.codearte.accurest.dsl.internal.MatchingStrategy +import io.codearte.accurest.dsl.internal.QueryParameter +import io.codearte.accurest.dsl.internal.Request +import io.codearte.accurest.dsl.internal.Response +import io.codearte.accurest.dsl.internal.UrlPath import io.codearte.accurest.util.ContentType import io.codearte.accurest.util.JsonConverter import java.util.regex.Pattern -import static io.codearte.accurest.util.ContentUtils.* +import static io.codearte.accurest.util.ContentUtils.extractValue +import static io.codearte.accurest.util.ContentUtils.recognizeContentTypeFromContent +import static io.codearte.accurest.util.ContentUtils.recognizeContentTypeFromHeader /** * @author Jakub Kubrynski */ @PackageScope class SpockMethodBodyBuilder { - private final GroovyDsl stubDefinition + private final GroovyDsl stubDefinition - SpockMethodBodyBuilder(GroovyDsl stubDefinition) { - this.stubDefinition = stubDefinition - } + SpockMethodBodyBuilder(GroovyDsl stubDefinition) { + this.stubDefinition = stubDefinition + } - void appendTo(BlockBuilder blockBuilder) { - Request request = stubDefinition.request - Response response = stubDefinition.response - blockBuilder.with { - startBlock() - addLine('given:').startBlock() - addLine('def request = given()') - indent() - request.headers?.collect { Header header -> - addLine(".header('${header.name}', '${header.serverValue}')") - } - if (request.body) { - Object bodyValue = extractServerValueFromBody(request.body.serverValue) - String matches = trimRepeatedQuotes(new JsonOutput().toJson(bodyValue)) - addLine(".body('$matches')") - } + void appendTo(BlockBuilder blockBuilder) { + Request request = stubDefinition.request + Response response = stubDefinition.response + blockBuilder.with { + startBlock() + addLine('given:').startBlock() + addLine('def request = given()') + indent() + request.headers?.collect { Header header -> + addLine(".header('${header.name}', '${header.serverValue}')") + } + if (request.body) { + Object bodyValue = extractServerValueFromBody(request.body.serverValue) + String matches = trimRepeatedQuotes(new JsonOutput().toJson(bodyValue)) + addLine(".body('$matches')") + } - unindent().endBlock().addEmptyLine() + unindent().endBlock().addEmptyLine() - addLine('when:').startBlock() - addLine('def response = given().spec(request)') - indent() + addLine('when:').startBlock() + addLine('def response = given().spec(request)') + indent() - String url = buildUrl(request) - String method = request.method.serverValue.toLowerCase() + String url = buildUrl(request) + String method = request.method.serverValue.toLowerCase() - blockBuilder.addLine(/.${method}("$url")/) - unindent().endBlock().addEmptyLine() + blockBuilder.addLine(/.${method}("$url")/) + unindent().endBlock().addEmptyLine() - addLine('then:').startBlock() - addLine("response.statusCode == $response.status.serverValue") + addLine('then:').startBlock() + addLine("response.statusCode == $response.status.serverValue") - response.headers?.collect { Header header -> - addLine("response.header('$header.name') == '$header.serverValue'") - } - if (response.body) { - endBlock() - addLine('and:').startBlock() - def responseBody = response.body.serverValue - ContentType contentType = recognizeContentTypeFromHeader(response.headers) - if (contentType == ContentType.UNKNOWN) { - contentType = recognizeContentTypeFromContent(responseBody) - } - if (responseBody instanceof GString) { - responseBody = extractValue(responseBody, contentType, { DslProperty dslProperty -> dslProperty.serverValue }) - } - if (contentType == ContentType.JSON) { - addLine('def responseBody = new JsonSlurper().parseText(response.body.asString())') - if (responseBody instanceof List) { - processArrayElements(responseBody, "", blockBuilder) - } else { - processMapElement(responseBody, blockBuilder, "") - } - } else if (contentType == ContentType.XML) { - addLine('def responseBody = new XmlSlurper().parseText(response.body.asString())') - // TODO xml validation - } - } - endBlock() + response.headers?.collect { Header header -> + addLine("response.header('$header.name') == '$header.serverValue'") + } + if (response.body) { + endBlock() + addLine('and:').startBlock() + def responseBody = response.body.serverValue + ContentType contentType = recognizeContentTypeFromHeader(response.headers) + if (contentType == ContentType.UNKNOWN) { + contentType = recognizeContentTypeFromContent(responseBody) + } + if (responseBody instanceof GString) { + responseBody = extractValue(responseBody, contentType, { DslProperty dslProperty -> dslProperty.serverValue }) + } + if (contentType == ContentType.JSON) { + addLine('def responseBody = new JsonSlurper().parseText(response.body.asString())') + if (responseBody instanceof List) { + processArrayElements(responseBody, "", blockBuilder) + } else { + processMapElement(responseBody, blockBuilder, "") + } + } else if (contentType == ContentType.XML) { + addLine('def responseBody = new XmlSlurper().parseText(response.body.asString())') + // TODO xml validation + } + } + endBlock() - endBlock() - } - } + endBlock() + } + } - static String trimRepeatedQuotes(String toTrim) { - if (toTrim.startsWith('"')) { - return toTrim.replaceAll('"', '') - } - return toTrim - } + static String trimRepeatedQuotes(String toTrim) { + if (toTrim.startsWith('"')) { + return toTrim.replaceAll('"', '') + } + return toTrim + } - private Object extractServerValueFromBody(bodyValue) { - if (bodyValue instanceof GString) { - bodyValue = extractValue(bodyValue, { DslProperty dslProperty -> dslProperty.serverValue }) - } else { - bodyValue = JsonConverter.transformValues(bodyValue, { it instanceof DslProperty ? it.serverValue : it }) - } - return bodyValue - } + private Object extractServerValueFromBody(bodyValue) { + if (bodyValue instanceof GString) { + bodyValue = extractValue(bodyValue, { DslProperty dslProperty -> dslProperty.serverValue }) + } else { + bodyValue = JsonConverter.transformValues(bodyValue, { it instanceof DslProperty ? it.serverValue : it }) + } + return bodyValue + } - private String buildUrl(Request request) { - if (request.url) - return request.url.serverValue; - if (request.urlPath) - return buildUrlFromUrlPath(request.urlPath) - throw new IllegalStateException("URL is not set!") - } + private String buildUrl(Request request) { + if (request.url) + return request.url.serverValue; + if (request.urlPath) + return buildUrlFromUrlPath(request.urlPath) + throw new IllegalStateException("URL is not set!") + } - private String buildUrlFromUrlPath(UrlPath urlPath) { - String params = urlPath.queryParameters.parameters.inject([]) { result, param -> - result << "${param.name}=${URLEncoder.encode(resolveParamValue(param).toString(), "UTF8")}" - }.join('&') - return "$urlPath.serverValue?$params" - } + private String buildUrlFromUrlPath(UrlPath urlPath) { + String params = urlPath.queryParameters.parameters.inject([]) { result, param -> + result << "${param.name}=${URLEncoder.encode(resolveParamValue(param).toString(), "UTF8")}" + }.join('&') + return "$urlPath.serverValue?$params" + } - private String resolveParamValue(QueryParameter param) { - resolveParamValue(param.serverValue) - } + private String resolveParamValue(QueryParameter param) { + resolveParamValue(param.serverValue) + } - private String resolveParamValue(Object value) { - value.toString() - } + private String resolveParamValue(Object value) { + value.toString() + } - private String resolveParamValue(MatchingStrategy matchingStrategy) { - matchingStrategy.serverValue.toString() - } + private String resolveParamValue(MatchingStrategy matchingStrategy) { + matchingStrategy.serverValue.toString() + } - private void processBodyElement(BlockBuilder blockBuilder, String property, def value) { - if (value instanceof String) { - if (value.startsWith('$')) { - value = value.substring(1).replaceAll('\\$value', "responseBody$property") - blockBuilder.addLine(value) - } else { - blockBuilder.addLine("responseBody$property == \"${value}\"") - } - } else if (value instanceof Map) { - processMapElement(value, blockBuilder, property) - } else if (value instanceof Map.Entry) { - processEntryElement(blockBuilder, property, value) - } else if (value instanceof List) { - processArrayElements(value, property, blockBuilder) - } else if (value instanceof Pattern) { - blockBuilder.addLine("responseBody$property ==~ java.util.regex.Pattern.compile('${value}')") - } else if (value instanceof DslProperty) { - processBodyElement(blockBuilder, property, value.serverValue) - } else if (value instanceof ExecutionProperty) { - ExecutionProperty exec = (ExecutionProperty) value - blockBuilder.addLine("${exec.insertValue("responseBody$property")}") - } else { - blockBuilder.addLine("responseBody$property == ${value}") - } - } + private void processBodyElement(BlockBuilder blockBuilder, String property, def value) { + if (value instanceof String) { + if (value.startsWith('$')) { + value = value.substring(1).replaceAll('\\$value', "responseBody$property") + blockBuilder.addLine(value) + } else { + blockBuilder.addLine("responseBody$property == \"${value}\"") + } + } else if (value instanceof Map) { + processMapElement(value, blockBuilder, property) + } else if (value instanceof Map.Entry) { + processEntryElement(blockBuilder, property, value) + } else if (value instanceof List) { + processArrayElements(value, property, blockBuilder) + } else if (value instanceof Pattern) { + blockBuilder.addLine("responseBody$property ==~ java.util.regex.Pattern.compile('${value}')") + } else if (value instanceof DslProperty) { + processBodyElement(blockBuilder, property, value.serverValue) + } else if (value instanceof ExecutionProperty) { + ExecutionProperty exec = (ExecutionProperty) value + blockBuilder.addLine("${exec.insertValue("responseBody$property")}") + } else { + blockBuilder.addLine("responseBody$property == ${value}") + } + } - private void processMapElement(def value, BlockBuilder blockBuilder, String property) { - value.each { entry -> processEntryElement(blockBuilder, property, entry) } - } + private void processMapElement(def value, BlockBuilder blockBuilder, String property) { + value.each { entry -> processEntryElement(blockBuilder, property, entry) } + } - private def processEntryElement(BlockBuilder blockBuilder, String property, def entry) { - return processBodyElement(blockBuilder, property + "." + entry.key, entry.value) - } + private def processEntryElement(BlockBuilder blockBuilder, String property, def entry) { + return processBodyElement(blockBuilder, property + "." + entry.key, entry.value) + } - private void processArrayElements(List responseBody, String property, BlockBuilder blockBuilder) { - responseBody.eachWithIndex { - listElement, listIndex -> - listElement.each { entry -> - String prop = "$property[$listIndex]" ?: '' - processBodyElement(blockBuilder, prop, entry) - } - } - } + private void processArrayElements(List responseBody, String property, BlockBuilder blockBuilder) { + responseBody.eachWithIndex { + listElement, listIndex -> + listElement.each { entry -> + String prop = "$property[$listIndex]" ?: '' + processBodyElement(blockBuilder, prop, entry) + } + } + } } diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy index 35135dff7b..e9301d0c70 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy @@ -9,279 +9,279 @@ import spock.lang.Specification */ class SpockMethodBuilderSpec extends Specification { - def "should generate assertions for simple response body"() { - given: - GroovyDsl contractDsl = GroovyDsl.make { - request { - method "GET" - url "test" - } - response { - status 200 - body """{ + def "should generate assertions for simple response body"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "GET" + url "test" + } + response { + status 200 + body """{ "property1": "a", "property2": "b" }""" - } - } - SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) - BlockBuilder blockBuilder = new BlockBuilder(" ") - when: - builder.appendTo(blockBuilder) - then: - blockBuilder.toString().contains("responseBody.property1 == \"a\"") - blockBuilder.toString().contains("responseBody.property2 == \"b\"") - } + } + } + SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains("responseBody.property1 == \"a\"") + blockBuilder.toString().contains("responseBody.property2 == \"b\"") + } - @Issue("#79") - def "should generate assertions for simple response body constructed from map with a list"() { - given: - GroovyDsl contractDsl = GroovyDsl.make { - request { - method "GET" - url "test" - } - response { - status 200 - body( - property1: 'a', - property2: [ - [a: 'sth'], - [b: 'sthElse'] - ] - ) - } - } - SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) - BlockBuilder blockBuilder = new BlockBuilder(" ") - when: - builder.appendTo(blockBuilder) - then: - blockBuilder.toString().contains("responseBody.property1 == \"a\"") - blockBuilder.toString().contains("responseBody.property2[0].a == \"sth\"") - blockBuilder.toString().contains("responseBody.property2[1].b == \"sthElse\"") - } + @Issue("#79") + def "should generate assertions for simple response body constructed from map with a list"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "GET" + url "test" + } + response { + status 200 + body( + property1: 'a', + property2: [ + [a: 'sth'], + [b: 'sthElse'] + ] + ) + } + } + SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains("responseBody.property1 == \"a\"") + blockBuilder.toString().contains("responseBody.property2[0].a == \"sth\"") + blockBuilder.toString().contains("responseBody.property2[1].b == \"sthElse\"") + } - @Issue("#82") - def "should generate proper request when body constructed from map with a list"() { - given: - GroovyDsl contractDsl = GroovyDsl.make { - request { - method "GET" - url "test" - body( - items: ['HOP'] - ) - } - response { - status 200 - } - } - SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) - BlockBuilder blockBuilder = new BlockBuilder(" ") - when: - builder.appendTo(blockBuilder) - then: - blockBuilder.toString().contains(".body('{\"items\":[\"HOP\"]}')") - } + @Issue("#82") + def "should generate proper request when body constructed from map with a list"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "GET" + url "test" + body( + items: ['HOP'] + ) + } + response { + status 200 + } + } + SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains(".body('{\"items\":[\"HOP\"]}')") + } - @Issue("#88") - def "should generate proper request when body constructed from GString"() { - given: - GroovyDsl contractDsl = GroovyDsl.make { - request { - method "GET" - url "test" - body( - "property1=VAL1" - ) - } - response { - status 200 - } - } - SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) - BlockBuilder blockBuilder = new BlockBuilder(" ") - when: - builder.appendTo(blockBuilder) - then: - blockBuilder.toString().contains(".body('property1=VAL1')") - } + @Issue("#88") + def "should generate proper request when body constructed from GString"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "GET" + url "test" + body( + "property1=VAL1" + ) + } + response { + status 200 + } + } + SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains(".body('property1=VAL1')") + } - def "should generate assertions for array in response body"() { - given: - GroovyDsl contractDsl = GroovyDsl.make { - request { - method "GET" - url "test" - } - response { - status 200 - body """[ + def "should generate assertions for array in response body"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "GET" + url "test" + } + response { + status 200 + body """[ { "property1": "a" }, { "property2": "b" }]""" - } - } - SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) - BlockBuilder blockBuilder = new BlockBuilder(" ") - when: - builder.appendTo(blockBuilder) - then: - blockBuilder.toString().contains("responseBody[0].property1 == \"a\"") - blockBuilder.toString().contains("responseBody[1].property2 == \"b\"") - } + } + } + SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains("responseBody[0].property1 == \"a\"") + blockBuilder.toString().contains("responseBody[1].property2 == \"b\"") + } - def "should generate assertions for array inside response body element"() { - given: - GroovyDsl contractDsl = GroovyDsl.make { - request { - method "GET" - url "test" - } - response { - status 200 - body """{ + def "should generate assertions for array inside response body element"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "GET" + url "test" + } + response { + status 200 + body """{ "property1": [ { "property2": "test1"}, { "property3": "test2"} ] }""" - } - } - SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) - BlockBuilder blockBuilder = new BlockBuilder(" ") - when: - builder.appendTo(blockBuilder) - then: - blockBuilder.toString().contains("responseBody.property1[0].property2 == \"test1\"") - blockBuilder.toString().contains("responseBody.property1[1].property3 == \"test2\"") - } + } + } + SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains("responseBody.property1[0].property2 == \"test1\"") + blockBuilder.toString().contains("responseBody.property1[1].property3 == \"test2\"") + } - def "should generate assertions for nested objects in response body"() { - given: - GroovyDsl contractDsl = GroovyDsl.make { - request { - method "GET" - url "test" - } - response { - status 200 - body '''\ + def "should generate assertions for nested objects in response body"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "GET" + url "test" + } + response { + status 200 + body '''\ { "property1": "a", "property2": {"property3": "b"} } ''' - } - } - SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) - BlockBuilder blockBuilder = new BlockBuilder(" ") - when: - builder.appendTo(blockBuilder) - then: - blockBuilder.toString().contains("responseBody.property1 == \"a\"") - blockBuilder.toString().contains("responseBody.property2.property3 == \"b\"") - } + } + } + SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains("responseBody.property1 == \"a\"") + blockBuilder.toString().contains("responseBody.property2.property3 == \"b\"") + } - def "should generate regex assertions for map objects in response body"() { - given: - GroovyDsl contractDsl = GroovyDsl.make { - request { - method "GET" - url "test" - } - response { - status 200 - body( - property1: "a", - property2: value( - client('123'), - server(regex('[0-9]{3}')) - ) - ) - headers { - header('Content-Type': 'application/json') + def "should generate regex assertions for map objects in response body"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "GET" + url "test" + } + response { + status 200 + body( + property1: "a", + property2: value( + client('123'), + server(regex('[0-9]{3}')) + ) + ) + headers { + header('Content-Type': 'application/json') - } + } - } - } - SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) - BlockBuilder blockBuilder = new BlockBuilder(" ") - when: - builder.appendTo(blockBuilder) - then: - blockBuilder.toString().contains("responseBody.property1 == \"a\"") - blockBuilder.toString().contains("responseBody.property2 ==~ java.util.regex.Pattern.compile('[0-9]{3}')") - } + } + } + SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains("responseBody.property1 == \"a\"") + blockBuilder.toString().contains("responseBody.property2 ==~ java.util.regex.Pattern.compile('[0-9]{3}')") + } - def "should generate regex assertions for string objects in response body"() { - given: - GroovyDsl contractDsl = GroovyDsl.make { - request { - method "GET" - url "test" - } - response { - status 200 - body("""{"property1":"a","property2":"${value(client('123'), server(regex('[0-9]{3}')))}"}""") - headers { - header('Content-Type': 'application/json') + def "should generate regex assertions for string objects in response body"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "GET" + url "test" + } + response { + status 200 + body("""{"property1":"a","property2":"${value(client('123'), server(regex('[0-9]{3}')))}"}""") + headers { + header('Content-Type': 'application/json') - } + } - } - } - SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) - BlockBuilder blockBuilder = new BlockBuilder(" ") - when: - builder.appendTo(blockBuilder) - then: - blockBuilder.toString().contains("responseBody.property1 == \"a\"") - blockBuilder.toString().contains("responseBody.property2 ==~ java.util.regex.Pattern.compile('[0-9]{3}')") - } + } + } + SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains("responseBody.property1 == \"a\"") + blockBuilder.toString().contains("responseBody.property2 ==~ java.util.regex.Pattern.compile('[0-9]{3}')") + } - def "should generate a call with an url path and query parameters"() { - given: - GroovyDsl contractDsl = GroovyDsl.make { - request { - method 'GET' - urlPath('/users') { - queryParameters { - parameter 'limit': $(client(equalTo("20")), server(equalTo("10"))) - parameter 'offset': $(client(containing("20")), server(equalTo("20"))) - parameter 'filter': "email" - parameter 'sort': equalTo("name") - parameter 'search': $(client(notMatching(~/^\/[0-9]{2}$/)), server("55")) - parameter 'age': $(client(notMatching("^\\w*\$")), server("99")) - parameter 'name': $(client(matching("Denis.*")), server("Denis.Stepanov")) - } - } - } - response { - status 200 - body """ + def "should generate a call with an url path and query parameters"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method 'GET' + urlPath('/users') { + queryParameters { + parameter 'limit': $(client(equalTo("20")), server(equalTo("10"))) + parameter 'offset': $(client(containing("20")), server(equalTo("20"))) + parameter 'filter': "email" + parameter 'sort': equalTo("name") + parameter 'search': $(client(notMatching(~/^\/[0-9]{2}$/)), server("55")) + parameter 'age': $(client(notMatching("^\\w*\$")), server("99")) + parameter 'name': $(client(matching("Denis.*")), server("Denis.Stepanov")) + } + } + } + response { + status 200 + body """ { "property1": "a", "property2": "b" } """ - } - } - SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) - BlockBuilder blockBuilder = new BlockBuilder(" ") - when: - builder.appendTo(blockBuilder) - def spockTest = blockBuilder.toString() - then: - spockTest.contains('get("/users?limit=10&offset=20&filter=email&sort=name&search=55&age=99&name=Denis.Stepanov")') - spockTest.contains('responseBody.property1 == "a"') - spockTest.contains('responseBody.property2 == "b"') - } + } + } + SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + def spockTest = blockBuilder.toString() + then: + spockTest.contains('get("/users?limit=10&offset=20&filter=email&sort=name&search=55&age=99&name=Denis.Stepanov")') + spockTest.contains('responseBody.property1 == "a"') + spockTest.contains('responseBody.property2 == "b"') + } } From e2c956177878f035866a61d8a3dfb82a607463aa Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Thu, 18 Jun 2015 13:08:20 +0200 Subject: [PATCH 042/119] Changed method from static to private. --- .../io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy index e3c0b1388b..4da97ee58b 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy @@ -95,7 +95,7 @@ class SpockMethodBodyBuilder { } } - static String trimRepeatedQuotes(String toTrim) { + private String trimRepeatedQuotes(String toTrim) { if (toTrim.startsWith('"')) { return toTrim.replaceAll('"', '') } From f3773169b06b074f179d424c2bfd4650b108510b Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Thu, 18 Jun 2015 18:01:36 +0200 Subject: [PATCH 043/119] Removed unnecessary reference call. Changed the required version of apache-commons-lang3 for accurest-core to 3.3 as we use methods that were introduced in 3.3. --- .../plugin/GenerateWiremockClientStubsFromDslTask.groovy | 4 ++-- build.gradle | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/GenerateWiremockClientStubsFromDslTask.groovy b/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/GenerateWiremockClientStubsFromDslTask.groovy index 6a6f08f692..ab6f6473c1 100644 --- a/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/GenerateWiremockClientStubsFromDslTask.groovy +++ b/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/GenerateWiremockClientStubsFromDslTask.groovy @@ -17,8 +17,8 @@ class GenerateWiremockClientStubsFromDslTask extends ConventionTask { @TaskAction void generate() { - project.logger.info("Accurest Plugin: Invoking GroovyDSL to Wiremock client stubs conversion") - project.logger.debug("From '${getContractsDslDir()}' to '${getStubsOutputDir()}'") + logger.info("Accurest Plugin: Invoking GroovyDSL to Wiremock client stubs conversion") + logger.debug("From '${getContractsDslDir()}' to '${getStubsOutputDir()}'") RecursiveFilesConverter converter = new RecursiveFilesConverter(new DslToWiremockClientConverter(), getContractsDslDir(), getStubsOutputDir()) diff --git a/build.gradle b/build.gradle index 2ad0eb7a0c..3ae3d0e265 100644 --- a/build.gradle +++ b/build.gradle @@ -87,7 +87,7 @@ project(':accurest-core') { compile 'org.slf4j:slf4j-api:[1.6.0,)' compile 'org.codehaus.plexus:plexus-utils:[3.0.0,)' compile 'commons-io:commons-io:[2.0,)' - compile 'org.apache.commons:commons-lang3:[3.0,)' + compile 'org.apache.commons:commons-lang3:[3.3,)' testCompile 'cglib:cglib-nodep:2.2' testCompile 'org.objenesis:objenesis:2.1' testCompile 'com.github.tomakehurst:wiremock:1.53' From f74e86ce5d3f1d6cfa1c4666bfa11a78c9b266db Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Thu, 18 Jun 2015 18:44:57 +0200 Subject: [PATCH 044/119] [#86] Fixed the case when an exception is thrown for XML processing --- .../groovy/io/codearte/accurest/util/ContentUtils.groovy | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy index 6cab8d76fd..59b4d726ee 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy @@ -51,12 +51,19 @@ class ContentUtils { return extractValueForXML(bodyAsValue, valueProvider) } catch (Exception exception) { log.debug("No content type provided and failed to parse as XML - returning the value back to the user", exception) - return bodyAsValue + return extractValueForGString(bodyAsValue, valueProvider) } } } + private static GStringImpl extractValueForGString(GString bodyAsValue, Closure valueProvider) { + return new GStringImpl( + bodyAsValue.values.collect { it instanceof DslProperty ? valueProvider(it) : it } as String[], + bodyAsValue.strings.clone() as String[] + ) + } + public static Object extractValue(GString bodyAsValue, Closure valueProvider) { return extractValue(bodyAsValue, ContentType.UNKNOWN, valueProvider) } From 8b44fc046b004d37eff375f70a38c65658ee6d72 Mon Sep 17 00:00:00 2001 From: Denis Stepanov Date: Mon, 22 Jun 2015 18:21:16 +0200 Subject: [PATCH 045/119] Do not escape url values in spock tests --- .../io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy | 2 +- .../io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy index 4da97ee58b..cc35423d97 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy @@ -121,7 +121,7 @@ class SpockMethodBodyBuilder { private String buildUrlFromUrlPath(UrlPath urlPath) { String params = urlPath.queryParameters.parameters.inject([]) { result, param -> - result << "${param.name}=${URLEncoder.encode(resolveParamValue(param).toString(), "UTF8")}" + result << "${param.name}=${resolveParamValue(param).toString()}" }.join('&') return "$urlPath.serverValue?$params" } diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy index e9301d0c70..b38ec458b1 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy @@ -259,6 +259,7 @@ class SpockMethodBuilderSpec extends Specification { parameter 'search': $(client(notMatching(~/^\/[0-9]{2}$/)), server("55")) parameter 'age': $(client(notMatching("^\\w*\$")), server("99")) parameter 'name': $(client(matching("Denis.*")), server("Denis.Stepanov")) + parameter 'email': "bob@email.com" } } } @@ -278,7 +279,7 @@ class SpockMethodBuilderSpec extends Specification { builder.appendTo(blockBuilder) def spockTest = blockBuilder.toString() then: - spockTest.contains('get("/users?limit=10&offset=20&filter=email&sort=name&search=55&age=99&name=Denis.Stepanov")') + spockTest.contains('get("/users?limit=10&offset=20&filter=email&sort=name&search=55&age=99&name=Denis.Stepanov&email=bob@email.com")') spockTest.contains('responseBody.property1 == "a"') spockTest.contains('responseBody.property2 == "b"') } From 3778a66a2a61044c4dca9e976e71beb52eae821b Mon Sep 17 00:00:00 2001 From: The Gitter Badger Date: Thu, 25 Jun 2015 07:51:54 +0000 Subject: [PATCH 046/119] Added Gitter badge --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 7cead077d6..9420cca7a0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ Accurate REST ============= +[![Join the chat at https://gitter.im/Codearte/accurest](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Codearte/accurest?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + [![Build Status](https://travis-ci.org/Codearte/accurest.svg?branch=master)](https://travis-ci.org/Codearte/accurest) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.codearte.accurest/accurest-gradle-plugin/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.codearte.accurest/accurest-gradle-plugin) Consumer Driven Contracts verifier for Java From 3370b2ee3c06af1db780c54428774a054e63e357 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Thu, 25 Jun 2015 09:53:39 +0200 Subject: [PATCH 047/119] Fixed the Gitter badge location --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 9420cca7a0..d379b16aa0 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,8 @@ Accurate REST ============= -[![Join the chat at https://gitter.im/Codearte/accurest](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Codearte/accurest?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) - [![Build Status](https://travis-ci.org/Codearte/accurest.svg?branch=master)](https://travis-ci.org/Codearte/accurest) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.codearte.accurest/accurest-gradle-plugin/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.codearte.accurest/accurest-gradle-plugin) +[![Join the chat at https://gitter.im/Codearte/accurest](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Codearte/accurest?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) Consumer Driven Contracts verifier for Java From 397617101c68bba0aeb8d0090c1f8502f67da8c3 Mon Sep 17 00:00:00 2001 From: Denis Stepanov Date: Mon, 22 Jun 2015 16:26:41 +0200 Subject: [PATCH 048/119] Generate JSON with full body --- .../dsl/WiremockRequestStubStrategy.groovy | 89 +++++++++++-------- .../accurest/util/RegexpBuilders.groovy | 72 +++++++++++++++ .../accurest/dsl/WiremockGroovyDslSpec.groovy | 73 ++++++++++++--- 3 files changed, 183 insertions(+), 51 deletions(-) create mode 100644 accurest-core/src/main/groovy/io/codearte/accurest/util/RegexpBuilders.groovy diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy index ea0b6b4a9f..a98088b672 100755 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy @@ -1,5 +1,4 @@ package io.codearte.accurest.dsl - import groovy.transform.PackageScope import groovy.transform.TypeChecked import io.codearte.accurest.dsl.internal.Body @@ -13,11 +12,12 @@ import io.codearte.accurest.util.ContentType import java.util.regex.Pattern -import static io.codearte.accurest.util.ContentUtils.extractValue -import static io.codearte.accurest.util.ContentUtils.recognizeContentTypeFromHeader import static io.codearte.accurest.util.ContentUtils.getEqualsTypeFromContentType import static io.codearte.accurest.util.ContentUtils.recognizeContentTypeFromContent +import static io.codearte.accurest.util.ContentUtils.recognizeContentTypeFromHeader import static io.codearte.accurest.util.ContentUtils.recognizeContentTypeFromMatchingStrategy +import static io.codearte.accurest.util.RegexpBuilders.buildGStringRegexpMatch +import static io.codearte.accurest.util.RegexpBuilders.buildJSONRegexpMatch @TypeChecked @PackageScope @@ -93,58 +93,73 @@ class WiremockRequestStubStrategy extends BaseWiremockStubStrategy { return [appendBodyPattern(matchingStrategy)] } - private List> appendBodyPatterns(List matchingStrategies) { - return matchingStrategies.collect { appendBodyPattern(it) } - } - - private List> appendBodyPatterns(GString gString) { - if (containsPattern(gString)) { - Object value = extractValue(gString, { DslProperty dslProperty -> dslProperty.clientValue }) - return appendBodyPatterns(extractReqexpMatching(value)) - } - return appendBodyPatterns(new MatchingStrategy(gString, getEqualsTypeFromContentTypeHeader())) - } - private List> appendBodyPatterns(Object bodyValue) { - return appendBodyPatterns(new MatchingStrategy(bodyValue, MatchingStrategy.Type.EQUAL_TO)) + return appendBodyPatterns(new MatchingStrategy(bodyValue, getEqualsTypeFromContentTypeHeader())) } private Map appendBodyPattern(MatchingStrategy matchingStrategy) { MatchingStrategy.Type type = matchingStrategy.type - Object value= matchingStrategy.clientValue + Object value = matchingStrategy.clientValue ContentType contentType = recognizeContentTypeFromMatchingStrategy(type) if (contentType == ContentType.UNKNOWN && type == MatchingStrategy.Type.EQUAL_TO) { contentType = recognizeContentTypeFromContent(value) type = getEqualsTypeFromContentType(contentType) } - Map result = [(type.name): parseBody(value, contentType)] - if (type == MatchingStrategy.Type.EQUAL_TO_JSON && matchingStrategy.jsonCompareMode) { + if (containsPattern(value)) { + return appendBodyRegexpMatchPattern(value, contentType) + } + return buildMatchPattern(new MatchingStrategy(parseBody(value, contentType), type)) + } + + private Map appendBodyRegexpMatchPattern(Object value, ContentType contentType) { + switch (contentType) { + case ContentType.JSON: + return buildMatchPattern(new MatchingStrategy(buildJSONRegexpMatch(value), MatchingStrategy.Type.MATCHING)) + case ContentType.UNKNOWN: + return buildMatchPattern(new MatchingStrategy(buildGStringRegexpMatch(value), MatchingStrategy.Type.MATCHING)) + case ContentType.XML: + throw new IllegalStateException("XML pattern matching is not implemented yet") + } + } + + private Map buildMatchPattern(MatchingStrategy matchingStrategy) { + Map result = [(matchingStrategy.type.name): matchingStrategy.clientValue.toString()] + if (matchingStrategy.type == MatchingStrategy.Type.EQUAL_TO_JSON && matchingStrategy.jsonCompareMode) { return result << [jsonCompareMode : (matchingStrategy.jsonCompareMode.toString())] } return result } private boolean containsPattern(GString bodyAsValue) { - return bodyAsValue.values.collect { it instanceof DslProperty ? it.clientValue : it } - .find { it instanceof Pattern } + return containsPattern(bodyAsValue.values) } - private List extractReqexpMatching(Object responseBodyObject) { - def matchingStrategies = new ArrayList() - if (responseBodyObject instanceof GString) { - return [new MatchingStrategy(responseBodyObject, MatchingStrategy.Type.MATCHING)] - } else if (responseBodyObject instanceof Map) { - responseBodyObject.each { k, v -> - if (v instanceof List) { - v.each { - matchingStrategies.addAll(extractReqexpMatching((Map)it)) - } - } else { - matchingStrategies.add(new MatchingStrategy(/.*${k}":.?"?${v}"?.*/, MatchingStrategy.Type.MATCHING)) - } - } - } - return matchingStrategies + private boolean containsPattern(Map map) { + return containsPattern(map.entrySet()) + } + + private boolean containsPattern(Collection collection) { + return collection.collect(this.&containsPattern).inject { a, b -> a || b } + } + + private boolean containsPattern(Object[] objects) { + return containsPattern(objects.toList()) + } + + private boolean containsPattern(Map.Entry entry) { + return containsPattern(entry.value) + } + + private boolean containsPattern(DslProperty dslProperty) { + return containsPattern(dslProperty.clientValue) + } + + private boolean containsPattern(Pattern pattern) { + return true + } + + private boolean containsPattern(Object o) { + return false } private MatchingStrategy.Type getEqualsTypeFromContentTypeHeader() { diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/util/RegexpBuilders.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/RegexpBuilders.groovy new file mode 100644 index 0000000000..47cb595220 --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/RegexpBuilders.groovy @@ -0,0 +1,72 @@ +package io.codearte.accurest.util + +import io.codearte.accurest.dsl.internal.DslProperty +import org.codehaus.groovy.runtime.GStringImpl + +import java.util.regex.Pattern + +import static io.codearte.accurest.util.ContentUtils.extractValue +import static org.apache.commons.lang3.StringEscapeUtils.escapeJson + +public class RegexpBuilders { + + public static String buildGStringRegexpMatch(GString gString) { + new GStringImpl( + gString.values.collect(this.&buildGStringRegexpMatch) as Object[], + gString.strings.collect(this.&escapeSpecialRegexChars) as String[] + ) + } + + public static String buildGStringRegexpMatch(Pattern pattern) { + return pattern.pattern() + } + + public static String buildGStringRegexpMatch(DslProperty dslProperty) { + return buildGStringRegexpMatch(dslProperty.clientValue) + } + + public static String buildGStringRegexpMatch(Object o) { + return escapeSpecialRegexChars(o.toString()) + } + + private final static Pattern SPECIAL_REGEX_CHARS = Pattern.compile('[{}()\\[\\].+*?^$\\\\|]') + + private static String escapeSpecialRegexChars(String str) { + return SPECIAL_REGEX_CHARS.matcher(str).replaceAll('\\\\$0') + } + + private final static String WS = /\s*/ + + public static String buildJSONRegexpMatch(GString gString) { + return buildJSONRegexpMatch(extractValue(gString, ContentType.JSON, { DslProperty dslProperty -> dslProperty.clientValue })) + } + + public static String buildJSONRegexpMatch(Map jsonMap) { + return WS + "\\{" + jsonMap.collect(this.&buildJSONRegexpMatch).join(",") + "\\}" + WS + } + + public static String buildJSONRegexpMatch(List jsonList) { + return WS + "\\[" + jsonList.collect(this.&buildJSONRegexpMatch).join(",") + "\\]" + WS + } + + public static String buildJSONRegexpMatch(Map.Entry entry) { + return buildJSONRegexpMatchString(escapeJson(entry.key)) + ":" + buildJSONRegexpMatch(entry.value) + } + + public static String buildJSONRegexpMatch(Object value) { + return buildJSONRegexpMatchStringOptionalQuotes(escapeJson(value.toString())) + } + + public static String buildJSONRegexpMatch(Pattern pattern) { + return buildJSONRegexpMatchStringOptionalQuotes(pattern.pattern()) + } + + public static String buildJSONRegexpMatchString(String value) { + return WS + '"' + value + '"' + WS + } + + public static String buildJSONRegexpMatchStringOptionalQuotes(String value) { + return WS + '"?' + value + '"?' + WS + } + +} diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy index 80540ec70d..946e72064c 100755 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy @@ -553,7 +553,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { "method": "GET", "urlPattern": "/[0-9]{2}", "bodyPatterns": [ - {"matches": ".*personalId\\":.?\\"?^[0-9]{11}$\\"?.*"} + {"matches": "\\\\s*\\\\{\\\\s*\\\"personalId\\\"\\\\s*:\\\\s*\\\"?^[0-9]{11}$\\\"?\\\\s*\\\\}\\\\s*"} ] }, "response": { @@ -613,8 +613,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { }, "url": "/fraudcheck", "bodyPatterns": [ - {"matches": ".*clientPesel\\":.?\\"?[0-9]{10}\\"?.*"}, - {"matches": ".*loanAmount\\":.?\\"?123.123\\"?.*"} + {"matches": "\\\\s*\\\\{\\\\s*\\"clientPesel\\"\\\\s*:\\\\s*\\"?[0-9]{10}\\"?\\\\s*,\\\\s*\\"loanAmount\\"\\\\s*:\\\\s*\\"?123.123\\"?\\\\s*\\\\}\\\\s*"} ] }, "response": { @@ -986,17 +985,11 @@ class WiremockGroovyDslSpec extends WiremockSpec { "request": { "method": "GET", "urlPattern": "/[0-9]{2}", - "bodyPatterns": [ - {"matches": ".*birthDate\\":.?\\"?[0-9]{4}-[0-9]{2}-[0-9]{2}\\"?.*"}, - {"matches": ".*propertyName\\":.?\\"?[0-9]{2}\\"?.*"}, - {"matches": ".*providerValue\\":.?\\"?Test\\"?.*"}, - {"matches": ".*propertyName\\":.?\\"?[0-9]{2}\\"?.*"}, - {"matches": ".*providerValue\\":.?\\"?Test\\"?.*"}, - {"matches": ".*firstName\\":.?\\"?.*\\"?.*"}, - {"matches": ".*lastName\\":.?\\"?.*\\"?.*"}, - {"matches": ".*personalId\\":.?\\"?[0-9]{11}\\"?.*"} - ] - }, + "bodyPatterns": [ + { + "matches": "\\\\s*\\\\{\\\\s*\\"birthDate\\"\\\\s*:\\\\s*\\"?[0-9]{4}-[0-9]{2}-[0-9]{2}\\"?\\\\s*,\\\\s*\\"errors\\"\\\\s*:\\\\s*\\\\[\\\\s*\\\\{\\\\s*\\"propertyName\\"\\\\s*:\\\\s*\\"?[0-9]{2}\\"?\\\\s*,\\\\s*\\"providerValue\\"\\\\s*:\\\\s*\\"?Test\\"?\\\\s*\\\\}\\\\s*,\\\\s*\\\\{\\\\s*\\"propertyName\\"\\\\s*:\\\\s*\\"?[0-9]{2}\\"?\\\\s*,\\\\s*\\"providerValue\\"\\\\s*:\\\\s*\\"?Test\\"?\\\\s*\\\\}\\\\s*\\\\]\\\\s*,\\\\s*\\"firstName\\"\\\\s*:\\\\s*\\"?.*\\"?\\\\s*,\\\\s*\\"lastName\\"\\\\s*:\\\\s*\\"?.*\\"?\\\\s*,\\\\s*\\"personalId\\"\\\\s*:\\\\s*\\"?[0-9]{11}\\"?\\\\s*\\\\}\\\\s*" + } + ] }, "response": { "status": 200, "body": "{\\"name\\":\\"Jan\\"}", @@ -1008,6 +1001,58 @@ class WiremockGroovyDslSpec extends WiremockSpec { ''') } + def 'should use regexp matches when request body match is defined using a map with a pattern'() { + given: + GroovyDsl groovyDsl = GroovyDsl.make { + request { + method 'POST' + url '/reissue-payment-order' + body( + loanNumber: "999997001", + amount: value(client(regex('[0-9.]+')), server('100.00')), + currency: "DKK", + applicationName: value(client(regex('.*')), server("Auto-Repayments")), + username: value(client(regex('.*')), server("scheduler")), + cardId: 1 + ) + } + response { + status 200 + body ''' + { + "status": "OK" + } + ''' + headers { + header 'Content-Type': 'application/json' + } + } + } + when: + def json = toWiremockClientJsonStub(groovyDsl) + then: + parseJson(json) == parseJson(''' + { + "request": { + "method": "POST", + "url": "/reissue-payment-order", + "bodyPatterns": [ + { + "matches": "\\\\s*\\\\{\\\\s*\\"loanNumber\\"\\\\s*:\\\\s*\\"?999997001\\"?\\\\s*,\\\\s*\\"amount\\"\\\\s*:\\\\s*\\"?[0-9.]+\\"?\\\\s*,\\\\s*\\"currency\\"\\\\s*:\\\\s*\\"?DKK\\"?\\\\s*,\\\\s*\\"applicationName\\"\\\\s*:\\\\s*\\"?.*\\"?\\\\s*,\\\\s*\\"username\\"\\\\s*:\\\\s*\\"?.*\\"?\\\\s*,\\\\s*\\"cardId\\"\\\\s*:\\\\s*\\"?1\\"?\\\\s*\\\\}\\\\s*" + } + ] + }, + "response": { + "status": 200, + "body": "{\\"status\\":\\"OK\\"}", + "headers": { + "Content-Type": "application/json" + } + } + } + ''') + } + String toJsonString(value) { new JsonBuilder(value).toPrettyString() } From 6b2de1b0c00505c3bd2547412dac650d478b1e8d Mon Sep 17 00:00:00 2001 From: Denis Stepanov Date: Tue, 23 Jun 2015 15:27:53 +0200 Subject: [PATCH 049/119] Added absent matching strategy --- .../builder/SpockMethodBodyBuilder.groovy | 16 +++++- .../dsl/BaseWiremockStubStrategy.groovy | 4 ++ .../accurest/dsl/internal/Common.groovy | 22 ++++++++ .../dsl/internal/MatchingStrategy.groovy | 2 +- .../accurest/dsl/internal/Request.groovy | 4 ++ .../accurest/util/ValidateUtils.groovy | 7 ++- .../builder/SpockMethodBuilderSpec.groovy | 2 + .../accurest/dsl/WiremockGroovyDslSpec.groovy | 54 +++++++++++++++++++ 8 files changed, 108 insertions(+), 3 deletions(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy index cc35423d97..7e7bb697bc 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy @@ -120,12 +120,26 @@ class SpockMethodBodyBuilder { } private String buildUrlFromUrlPath(UrlPath urlPath) { - String params = urlPath.queryParameters.parameters.inject([]) { result, param -> + String params = urlPath.queryParameters.parameters + .findAll(this.&allowedQueryParameter) + .inject([]) { result, param -> result << "${param.name}=${resolveParamValue(param).toString()}" }.join('&') return "$urlPath.serverValue?$params" } + private boolean allowedQueryParameter(QueryParameter param) { + return allowedQueryParameter(param.serverValue) + } + + private boolean allowedQueryParameter(MatchingStrategy matchingStrategy) { + return matchingStrategy.type != MatchingStrategy.Type.ABSENT + } + + private boolean allowedQueryParameter(Object o) { + return true + } + private String resolveParamValue(QueryParameter param) { resolveParamValue(param.serverValue) } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/BaseWiremockStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/BaseWiremockStubStrategy.groovy index a5c100341b..e3285a9467 100755 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/BaseWiremockStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/BaseWiremockStubStrategy.groovy @@ -53,6 +53,10 @@ abstract class BaseWiremockStubStrategy { return parseBody(value.toString(), contentType) } + public Boolean parseBody(Boolean value, ContentType contentType) { + return value + } + public String parseBody(Map map, ContentType contentType) { def transformedMap = transformValues(map, transform) return parseBody(toJson(transformedMap), contentType) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Common.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Common.groovy index 3dca0390ef..87d509de59 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Common.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Common.groovy @@ -88,6 +88,28 @@ class Common { assert firstSide ==~ secondSide } + void assertThatSidesMatch(MatchingStrategy firstSide, MatchingStrategy secondSide) { + if (firstSide.type == MatchingStrategy.Type.ABSENT && secondSide != MatchingStrategy.Type.ABSENT) { + throwAbsentError() + } + } + + void assertThatSidesMatch(MatchingStrategy firstSide, Object secondSide) { + if (firstSide.type == MatchingStrategy.Type.ABSENT) { + throwAbsentError() + } + } + + void assertThatSidesMatch(Object firstSide, MatchingStrategy secondSide) { + if (secondSide.type == MatchingStrategy.Type.ABSENT) { + throwAbsentError() + } + } + + private void throwAbsentError() { + throw new IllegalStateException("Absent cannot only be used only on one side") + } + void assertThatSidesMatch(Object firstSide, Object secondSide) { // do nothing } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/MatchingStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/MatchingStrategy.groovy index e9093dc9aa..c31c0b4bf6 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/MatchingStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/MatchingStrategy.groovy @@ -35,7 +35,7 @@ class MatchingStrategy extends DslProperty { enum Type { EQUAL_TO("equalTo"), CONTAINS("contains"), MATCHING("matches"), NOT_MATCHING("doesNotMatch"), - EQUAL_TO_JSON("equalToJson"), EQUAL_TO_XML("equalToXml") + EQUAL_TO_JSON("equalToJson"), EQUAL_TO_XML("equalToXml"), ABSENT("absent") final String name diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Request.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Request.groovy index 1ed604d4fb..9be791488a 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Request.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Request.groovy @@ -125,6 +125,10 @@ class Request extends Common { return new MatchingStrategy(value, MatchingStrategy.Type.EQUAL_TO_JSON) } + MatchingStrategy absent() { + return new MatchingStrategy(true, MatchingStrategy.Type.ABSENT) + } + } @CompileStatic diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/util/ValidateUtils.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/ValidateUtils.groovy index eac7bce25c..7cb6e0f0ef 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/util/ValidateUtils.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/ValidateUtils.groovy @@ -6,6 +6,9 @@ import io.codearte.accurest.dsl.internal.MatchingStrategy import java.util.regex.Pattern +import static io.codearte.accurest.dsl.internal.MatchingStrategy.Type.ABSENT +import static io.codearte.accurest.dsl.internal.MatchingStrategy.Type.EQUAL_TO + @TypeChecked class ValidateUtils { @@ -23,8 +26,10 @@ class ValidateUtils { throw new IllegalStateException("$msg can't be a pattern for the server side") } + static List ALLOWED_MATCHING_TYPES_ON_SERVER_SIDE = [EQUAL_TO, ABSENT] + static void validateServerValue(MatchingStrategy matchingStrategy, String msg) { - if (matchingStrategy.type != MatchingStrategy.Type.EQUAL_TO) { + if (!ALLOWED_MATCHING_TYPES_ON_SERVER_SIDE.contains(matchingStrategy.type)) { throw new IllegalStateException("$msg can't be of a matching type: $matchingStrategy.type for the server side") } validateServerValue(matchingStrategy.serverValue, msg) diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy index b38ec458b1..964f03811b 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy @@ -260,6 +260,8 @@ class SpockMethodBuilderSpec extends Specification { parameter 'age': $(client(notMatching("^\\w*\$")), server("99")) parameter 'name': $(client(matching("Denis.*")), server("Denis.Stepanov")) parameter 'email': "bob@email.com" + parameter 'hello': $(client(matching("Denis.*")), server(absent())) + parameter 'hello': absent() } } } diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy index 80540ec70d..5e0b681604 100755 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy @@ -695,6 +695,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { parameter 'search': $(client(notMatching(~/^\/[0-9]{2}$/)), server("10")) parameter 'age': $(client(notMatching("^\\w*\$")), server(10)) parameter 'name': $(client(matching("Denis.*")), server("Denis")) + parameter 'credit': absent() } } } @@ -731,6 +732,9 @@ class WiremockGroovyDslSpec extends WiremockSpec { }, "name": { "matches": "Denis.*" + }, + "credit": { + "absent": true } } }, @@ -863,6 +867,56 @@ class WiremockGroovyDslSpec extends WiremockSpec { e.message.contains "Query parameter 'age' can't be of a matching type: NOT_MATCHING for the server side" } + def "should not allow query parameter with a different absent variation for server/client"() { + when: + GroovyDsl.make dsl + then: + def e = thrown(IllegalStateException) + e.message.contains "Absent cannot only be used only on one side" + where: + dsl << [ + { + request { + method 'GET' + urlPath("users") { + queryParameters { + parameter 'name': $(client(absent()), server("")) + } + } + } + response { + status 200 + } + }, + { + request { + method 'GET' + urlPath("users") { + queryParameters { + parameter 'name': $(client(""), server(absent())) + } + } + } + response { + status 200 + } + }, + { + request { + method 'GET' + urlPath("users") { + queryParameters { + parameter 'name': $(client(absent()), server(matching("abc"))) + } + } + } + response { + status 200 + } + } + ] + } + def "should generate request with url and queryParameters for client side"() { given: GroovyDsl groovyDsl = GroovyDsl.make { From 0eebda74394c433e7bc7df69fbb2931b433100da Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Thu, 25 Jun 2015 16:18:49 +0200 Subject: [PATCH 050/119] Fixed references to WireMock in the project so that it is correctly capitalized. --- README.md | 2 +- .../DslToWireMockClientConverter.groovy | 13 ++ ...r.groovy => DslToWireMockConverter.groovy} | 2 +- .../DslToWiremockClientConverter.groovy | 13 -- ...r.groovy => WireMockToDslConverter.groovy} | 22 ++-- ...> DslToWireMockClientConverterSpec.groovy} | 10 +- ...oovy => WireMockToDslConverterSpec.groovy} | 84 ++++++------ .../config/AccurestConfigProperties.groovy | 2 +- ...groovy => BaseWireMockStubStrategy.groovy} | 2 +- ...ovy => WireMockRequestStubStrategy.groovy} | 4 +- ...vy => WireMockResponseStubStrategy.groovy} | 4 +- .../accurest/dsl/WireMockStubStrategy.groovy | 21 +++ .../accurest/dsl/WiremockStubStrategy.groovy | 21 --- ...y => WireMockGroovyDslResponseSpec.groovy} | 6 +- ...ec.groovy => WireMockGroovyDslSpec.groovy} | 120 +++++++++--------- ...iremockSpec.groovy => WireMockSpec.groovy} | 4 +- .../plugin/AccurestGradlePlugin.groovy | 19 ++- ...rateWireMockClientStubsFromDslTask.groovy} | 8 +- .../plugin/BasicFunctionalSpec.groovy | 14 +- .../functionalTest/bootSimple/build.gradle | 14 +- .../functionalTest/sampleProject/build.gradle | 9 +- build.gradle | 1 + 22 files changed, 203 insertions(+), 192 deletions(-) create mode 100644 accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/DslToWireMockClientConverter.groovy rename accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/{DslToWiremockConverter.groovy => DslToWireMockConverter.groovy} (89%) delete mode 100644 accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/DslToWiremockClientConverter.groovy rename accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/{WiremockToDslConverter.groovy => WireMockToDslConverter.groovy} (88%) rename accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/{DslToWiremockClientConverterSpec.groovy => DslToWireMockClientConverterSpec.groovy} (90%) rename accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/{WiremockToDslConverterSpec.groovy => WireMockToDslConverterSpec.groovy} (79%) rename accurest-core/src/main/groovy/io/codearte/accurest/dsl/{BaseWiremockStubStrategy.groovy => BaseWireMockStubStrategy.groovy} (98%) rename accurest-core/src/main/groovy/io/codearte/accurest/dsl/{WiremockRequestStubStrategy.groovy => WireMockRequestStubStrategy.groovy} (98%) rename accurest-core/src/main/groovy/io/codearte/accurest/dsl/{WiremockResponseStubStrategy.groovy => WireMockResponseStubStrategy.groovy} (92%) create mode 100644 accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockStubStrategy.groovy delete mode 100644 accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockStubStrategy.groovy rename accurest-core/src/test/groovy/io/codearte/accurest/dsl/{WiremockGroovyDslResponseSpec.groovy => WireMockGroovyDslResponseSpec.groovy} (82%) rename accurest-core/src/test/groovy/io/codearte/accurest/dsl/{WiremockGroovyDslSpec.groovy => WireMockGroovyDslSpec.groovy} (87%) rename accurest-core/src/test/groovy/io/codearte/accurest/dsl/{WiremockSpec.groovy => WireMockSpec.groovy} (76%) rename accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/{GenerateWiremockClientStubsFromDslTask.groovy => GenerateWireMockClientStubsFromDslTask.groovy} (74%) diff --git a/README.md b/README.md index d379b16aa0..6bf509470d 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Consumer Driven Contracts verifier for Java Just to make long story short - AccuREST is a tool for Consumer Driven Contract (CDC) development. AccuREST ships easy DSL for describing REST contracts for JVM-based applications. The contract DSL is used by AccuREST for two things: -generating Wiremock's JSON stub definitions, allowing rapid development of the consumer side, +generating WireMock's JSON stub definitions, allowing rapid development of the consumer side, generating Spock's acceptance tests for the server - to verify if your API implementation is compliant with the contract. By using AccuREST you can move TDD to an architecture level. diff --git a/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/DslToWireMockClientConverter.groovy b/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/DslToWireMockClientConverter.groovy new file mode 100644 index 0000000000..619bd8b235 --- /dev/null +++ b/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/DslToWireMockClientConverter.groovy @@ -0,0 +1,13 @@ +package io.codearte.accurest.wiremock + +import groovy.transform.CompileStatic +import io.codearte.accurest.dsl.WireMockStubStrategy + +@CompileStatic +class DslToWireMockClientConverter extends DslToWireMockConverter { + + @Override + String convertContent(String dslBody) { + return new WireMockStubStrategy(createGroovyDSLfromStringContent(dslBody)).toWireMockClientStub() + } +} diff --git a/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/DslToWiremockConverter.groovy b/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/DslToWireMockConverter.groovy similarity index 89% rename from accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/DslToWiremockConverter.groovy rename to accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/DslToWireMockConverter.groovy index 5804010090..9163724610 100644 --- a/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/DslToWiremockConverter.groovy +++ b/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/DslToWireMockConverter.groovy @@ -4,7 +4,7 @@ import groovy.transform.CompileStatic import io.codearte.accurest.dsl.GroovyDsl @CompileStatic -abstract class DslToWiremockConverter implements SingleFileConverter { +abstract class DslToWireMockConverter implements SingleFileConverter { @Override boolean canHandleFileName(String fileName) { diff --git a/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/DslToWiremockClientConverter.groovy b/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/DslToWiremockClientConverter.groovy deleted file mode 100644 index 8156f17cf6..0000000000 --- a/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/DslToWiremockClientConverter.groovy +++ /dev/null @@ -1,13 +0,0 @@ -package io.codearte.accurest.wiremock - -import groovy.transform.CompileStatic -import io.codearte.accurest.dsl.WiremockStubStrategy - -@CompileStatic -class DslToWiremockClientConverter extends DslToWiremockConverter { - - @Override - String convertContent(String dslBody) { - return new WiremockStubStrategy(createGroovyDSLfromStringContent(dslBody)).toWiremockClientStub() - } -} diff --git a/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/WiremockToDslConverter.groovy b/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/WireMockToDslConverter.groovy similarity index 88% rename from accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/WiremockToDslConverter.groovy rename to accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/WireMockToDslConverter.groovy index 543f9eff89..689edb3d9a 100644 --- a/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/WiremockToDslConverter.groovy +++ b/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/WireMockToDslConverter.groovy @@ -8,15 +8,15 @@ import nl.flotsam.xeger.Xeger import static org.apache.commons.lang3.StringEscapeUtils.escapeJava -class WiremockToDslConverter { - static String fromWiremockStub(String wiremockStringStub) { - return new WiremockToDslConverter().convertFromWiremockStub(wiremockStringStub) +class WireMockToDslConverter { + static String fromWireMockStub(String wireMockStringStub) { + return new WireMockToDslConverter().convertFromWireMockStub(wireMockStringStub) } - private String convertFromWiremockStub(String wiremockStringStub) { - Object wiremockStub = new JsonSlurper().parseText(wiremockStringStub) - def request = wiremockStub.request - def response = wiremockStub.response + private String convertFromWireMockStub(String wireMockStringStub) { + Object wireMockStub = new JsonSlurper().parseText(wireMockStringStub) + def request = wireMockStub.request + def response = wireMockStub.response def bodyPatterns = request.bodyPatterns String urlPattern = request.urlPattern return """\ @@ -146,8 +146,8 @@ class WiremockToDslConverter { if (!it.name.endsWith('json')) { return } - String dslFromWiremockStub = fromWiremockStub(it.text) - String dslWrappedWithFactoryMethod = wrapWithFactoryMethod(dslFromWiremockStub) + String dslFromWireMockStub = fromWireMockStub(it.text) + String dslWrappedWithFactoryMethod = wrapWithFactoryMethod(dslFromWireMockStub) File newGroovyFile = new File(it.parent, it.name.replaceAll('json', 'groovy')) println("Creating new groovy file [$newGroovyFile.path]") newGroovyFile.text = dslWrappedWithFactoryMethod @@ -158,10 +158,10 @@ class WiremockToDslConverter { } } - static String wrapWithFactoryMethod(String dslFromWiremockStub) { + static String wrapWithFactoryMethod(String dslFromWireMockStub) { return """\ ${GroovyDsl.name}.make { - $dslFromWiremockStub + $dslFromWireMockStub } """ } diff --git a/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/DslToWiremockClientConverterSpec.groovy b/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/DslToWireMockClientConverterSpec.groovy similarity index 90% rename from accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/DslToWiremockClientConverterSpec.groovy rename to accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/DslToWireMockClientConverterSpec.groovy index edf11a256f..0831dde57b 100755 --- a/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/DslToWiremockClientConverterSpec.groovy +++ b/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/DslToWireMockClientConverterSpec.groovy @@ -3,11 +3,11 @@ package io.codearte.accurest.wiremock import groovy.json.JsonSlurper import spock.lang.Specification -class DslToWiremockClientConverterSpec extends Specification { +class DslToWireMockClientConverterSpec extends Specification { - def "should convert DSL file to Wiremock JSON"() { + def "should convert DSL file to WireMock JSON"() { given: - def converter = new DslToWiremockClientConverter() + def converter = new DslToWireMockClientConverter() and: String dslBody = """ io.codearte.accurest.dsl.GroovyDsl.make { @@ -28,9 +28,9 @@ class DslToWiremockClientConverterSpec extends Specification { } - def "should convert DSL file with a nested list to Wiremock JSON"() { + def "should convert DSL file with a nested list to WireMock JSON"() { given: - def converter = new DslToWiremockClientConverter() + def converter = new DslToWireMockClientConverter() and: String dslBody = """ io.codearte.accurest.dsl.GroovyDsl.make { diff --git a/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/WiremockToDslConverterSpec.groovy b/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/WireMockToDslConverterSpec.groovy similarity index 79% rename from accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/WiremockToDslConverterSpec.groovy rename to accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/WireMockToDslConverterSpec.groovy index b147d9da93..9cb1e9813f 100755 --- a/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/WiremockToDslConverterSpec.groovy +++ b/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/WireMockToDslConverterSpec.groovy @@ -4,11 +4,11 @@ import com.github.tomakehurst.wiremock.stubbing.StubMapping import io.codearte.accurest.dsl.GroovyDsl import spock.lang.Specification -class WiremockToDslConverterSpec extends Specification { +class WireMockToDslConverterSpec extends Specification { - def 'should produce a Groovy DSL from Wiremock stub'() { + def 'should produce a Groovy DSL from WireMock stub'() { given: - String wiremockStub = '''\ + String wireMockStub = '''\ { "request": { "method": "GET", @@ -32,7 +32,7 @@ class WiremockToDslConverterSpec extends Specification { } ''' and: - stubMappingIsValidWiremockStub(wiremockStub) + stubMappingIsValidWireMockStub(wireMockStub) and: GroovyDsl expectedGroovyDsl = GroovyDsl.make { request { @@ -65,7 +65,7 @@ class WiremockToDslConverterSpec extends Specification { } } when: - String groovyDsl = WiremockToDslConverter.fromWiremockStub(wiremockStub) + String groovyDsl = WireMockToDslConverter.fromWireMockStub(wireMockStub) then: new GroovyShell(this.class.classLoader).evaluate( """ io.codearte.accurest.dsl.GroovyDsl.make { @@ -74,9 +74,9 @@ class WiremockToDslConverterSpec extends Specification { } - def 'should convert Wiremock stub with response body containing simple JSON'() { + def 'should convert WireMock stub with response body containing simple JSON'() { given: - String wiremockStub = '''\ + String wireMockStub = '''\ { "request": { "method": "DELETE", @@ -97,7 +97,7 @@ class WiremockToDslConverterSpec extends Specification { } ''' and: - stubMappingIsValidWiremockStub(wiremockStub) + stubMappingIsValidWireMockStub(wireMockStub) and: GroovyDsl expectedGroovyDsl = GroovyDsl.make { request { @@ -119,7 +119,7 @@ class WiremockToDslConverterSpec extends Specification { } } when: - String groovyDsl = WiremockToDslConverter.fromWiremockStub(wiremockStub) + String groovyDsl = WireMockToDslConverter.fromWireMockStub(wireMockStub) then: new GroovyShell(this.class.classLoader).evaluate( """ io.codearte.accurest.dsl.GroovyDsl.make { @@ -127,9 +127,9 @@ class WiremockToDslConverterSpec extends Specification { }""") == expectedGroovyDsl } - def 'should convert Wiremock stub with response body containing integer'() { + def 'should convert WireMock stub with response body containing integer'() { given: - String wiremockStub = '''\ + String wireMockStub = '''\ { "request": { "method": "POST", @@ -150,7 +150,7 @@ class WiremockToDslConverterSpec extends Specification { } ''' and: - stubMappingIsValidWiremockStub(wiremockStub) + stubMappingIsValidWireMockStub(wireMockStub) and: GroovyDsl expectedGroovyDsl = GroovyDsl.make { request { @@ -170,7 +170,7 @@ class WiremockToDslConverterSpec extends Specification { } } when: - String groovyDsl = WiremockToDslConverter.fromWiremockStub(wiremockStub) + String groovyDsl = WireMockToDslConverter.fromWireMockStub(wireMockStub) then: new GroovyShell(this.class.classLoader).evaluate( """ io.codearte.accurest.dsl.GroovyDsl.make { @@ -178,9 +178,9 @@ class WiremockToDslConverterSpec extends Specification { }""") == expectedGroovyDsl } - def 'should convert Wiremock stub with response body as a list'() { + def 'should convert WireMock stub with response body as a list'() { given: - String wiremockStub = '''\ + String wireMockStub = '''\ { "request": { "method": "POST", @@ -201,7 +201,7 @@ class WiremockToDslConverterSpec extends Specification { } ''' and: - stubMappingIsValidWiremockStub(wiremockStub) + stubMappingIsValidWireMockStub(wireMockStub) and: GroovyDsl expectedGroovyDsl = GroovyDsl.make { request { @@ -224,7 +224,7 @@ class WiremockToDslConverterSpec extends Specification { } } when: - String groovyDsl = WiremockToDslConverter.fromWiremockStub(wiremockStub) + String groovyDsl = WireMockToDslConverter.fromWireMockStub(wireMockStub) then: new GroovyShell(this.class.classLoader).evaluate( """ io.codearte.accurest.dsl.GroovyDsl.make { @@ -233,9 +233,9 @@ class WiremockToDslConverterSpec extends Specification { } - def 'should convert Wiremock stub with response body containing a nested list'() { + def 'should convert WireMock stub with response body containing a nested list'() { given: - String wiremockStub = '''\ + String wireMockStub = '''\ { "request": { "method": "POST", @@ -253,7 +253,7 @@ class WiremockToDslConverterSpec extends Specification { } ''' and: - stubMappingIsValidWiremockStub(wiremockStub) + stubMappingIsValidWireMockStub(wireMockStub) and: GroovyDsl expectedGroovyDsl = GroovyDsl.make { request { @@ -290,7 +290,7 @@ class WiremockToDslConverterSpec extends Specification { } } when: - String groovyDsl = WiremockToDslConverter.fromWiremockStub(wiremockStub) + String groovyDsl = WireMockToDslConverter.fromWireMockStub(wireMockStub) then: new GroovyShell(this.class.classLoader).evaluate( """ io.codearte.accurest.dsl.GroovyDsl.make { @@ -298,9 +298,9 @@ class WiremockToDslConverterSpec extends Specification { }""") == expectedGroovyDsl } - def 'should convert Wiremock stub with request body checking equality to Json'() { + def 'should convert WireMock stub with request body checking equality to Json'() { given: - String wiremockStub = '''\ + String wireMockStub = '''\ { "request": { "method": "POST", @@ -315,7 +315,7 @@ class WiremockToDslConverterSpec extends Specification { } ''' and: - stubMappingIsValidWiremockStub(wiremockStub) + stubMappingIsValidWireMockStub(wireMockStub) and: GroovyDsl expectedGroovyDsl = GroovyDsl.make { request { @@ -328,7 +328,7 @@ class WiremockToDslConverterSpec extends Specification { } } when: - String groovyDsl = WiremockToDslConverter.fromWiremockStub(wiremockStub) + String groovyDsl = WireMockToDslConverter.fromWireMockStub(wireMockStub) then: GroovyDsl evaluatedGroovyDsl = new GroovyShell(this.class.classLoader).evaluate( """ io.codearte.accurest.dsl.GroovyDsl.make { @@ -338,9 +338,9 @@ class WiremockToDslConverterSpec extends Specification { evaluatedGroovyDsl == expectedGroovyDsl } - def 'should convert Wiremock stub with request body checking matching to Json'() { + def 'should convert WireMock stub with request body checking matching to Json'() { given: - String wiremockStub = '''\ + String wireMockStub = '''\ { "request": { "method": "POST", @@ -355,7 +355,7 @@ class WiremockToDslConverterSpec extends Specification { } ''' and: - stubMappingIsValidWiremockStub(wiremockStub) + stubMappingIsValidWireMockStub(wireMockStub) and: GroovyDsl expectedGroovyDsl = GroovyDsl.make { request { @@ -368,7 +368,7 @@ class WiremockToDslConverterSpec extends Specification { } } when: - String groovyDsl = WiremockToDslConverter.fromWiremockStub(wiremockStub) + String groovyDsl = WireMockToDslConverter.fromWireMockStub(wireMockStub) then: GroovyDsl evaluatedGroovyDsl = new GroovyShell(this.class.classLoader).evaluate( """ io.codearte.accurest.dsl.GroovyDsl.make { @@ -378,9 +378,9 @@ class WiremockToDslConverterSpec extends Specification { evaluatedGroovyDsl == expectedGroovyDsl } - def 'should convert Wiremock stub with request body with equalToJson'() { + def 'should convert WireMock stub with request body with equalToJson'() { given: - String wiremockStub = '''\ + String wireMockStub = '''\ { "request" : { "url" : "/test", @@ -396,7 +396,7 @@ class WiremockToDslConverterSpec extends Specification { } ''' and: - stubMappingIsValidWiremockStub(wiremockStub) + stubMappingIsValidWireMockStub(wireMockStub) and: GroovyDsl expectedGroovyDsl = GroovyDsl.make { request { @@ -409,7 +409,7 @@ class WiremockToDslConverterSpec extends Specification { } } when: - String groovyDsl = WiremockToDslConverter.fromWiremockStub(wiremockStub) + String groovyDsl = WireMockToDslConverter.fromWireMockStub(wireMockStub) then: GroovyDsl evaluatedGroovyDsl = new GroovyShell(this.class.classLoader).evaluate( """ io.codearte.accurest.dsl.GroovyDsl.make { @@ -419,9 +419,9 @@ class WiremockToDslConverterSpec extends Specification { evaluatedGroovyDsl == expectedGroovyDsl } - def 'should convert Wiremock stub with request body with equalTo'() { + def 'should convert WireMock stub with request body with equalTo'() { given: - String wiremockStub = '''\ + String wireMockStub = '''\ { "request" : { "url" : "/test", @@ -436,7 +436,7 @@ class WiremockToDslConverterSpec extends Specification { } ''' and: - stubMappingIsValidWiremockStub(wiremockStub) + stubMappingIsValidWireMockStub(wireMockStub) and: GroovyDsl expectedGroovyDsl = GroovyDsl.make { request { @@ -449,7 +449,7 @@ class WiremockToDslConverterSpec extends Specification { } } when: - String groovyDsl = WiremockToDslConverter.fromWiremockStub(wiremockStub) + String groovyDsl = WireMockToDslConverter.fromWireMockStub(wireMockStub) then: GroovyDsl evaluatedGroovyDsl = new GroovyShell(this.class.classLoader).evaluate( """ io.codearte.accurest.dsl.GroovyDsl.make { @@ -459,9 +459,9 @@ class WiremockToDslConverterSpec extends Specification { evaluatedGroovyDsl == expectedGroovyDsl } - def 'should convert Wiremock stub with request body with matches'() { + def 'should convert WireMock stub with request body with matches'() { given: - String wiremockStub = '''\ + String wireMockStub = '''\ { "request" : { "url" : "/test", @@ -476,7 +476,7 @@ class WiremockToDslConverterSpec extends Specification { } ''' and: - stubMappingIsValidWiremockStub(wiremockStub) + stubMappingIsValidWireMockStub(wireMockStub) and: GroovyDsl expectedGroovyDsl = GroovyDsl.make { request { @@ -489,7 +489,7 @@ class WiremockToDslConverterSpec extends Specification { } } when: - String groovyDsl = WiremockToDslConverter.fromWiremockStub(wiremockStub) + String groovyDsl = WireMockToDslConverter.fromWireMockStub(wireMockStub) then: GroovyDsl evaluatedGroovyDsl = new GroovyShell(this.class.classLoader).evaluate( """ io.codearte.accurest.dsl.GroovyDsl.make { @@ -499,7 +499,7 @@ class WiremockToDslConverterSpec extends Specification { evaluatedGroovyDsl == expectedGroovyDsl } - void stubMappingIsValidWiremockStub(String mappingDefinition) { + void stubMappingIsValidWireMockStub(String mappingDefinition) { StubMapping.buildFrom(mappingDefinition) } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/config/AccurestConfigProperties.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/config/AccurestConfigProperties.groovy index 965f3bf84a..9d69c3ee8e 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/config/AccurestConfigProperties.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/config/AccurestConfigProperties.groovy @@ -23,7 +23,7 @@ class AccurestConfigProperties { File generatedTestSourcesDir /** - * Dir where the generated Wiremock stubs from Groovy DSL should be placed. + * Dir where the generated WireMock stubs from Groovy DSL should be placed. * You can then mention them in your packaging task to create jar with stubs */ File stubsOutputDir diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/BaseWiremockStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/BaseWireMockStubStrategy.groovy similarity index 98% rename from accurest-core/src/main/groovy/io/codearte/accurest/dsl/BaseWiremockStubStrategy.groovy rename to accurest-core/src/main/groovy/io/codearte/accurest/dsl/BaseWireMockStubStrategy.groovy index e3285a9467..0afb66273b 100755 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/BaseWiremockStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/BaseWireMockStubStrategy.groovy @@ -13,7 +13,7 @@ import static io.codearte.accurest.util.ContentUtils.extractValue import static io.codearte.accurest.util.JsonConverter.transformValues @TypeChecked -abstract class BaseWiremockStubStrategy { +abstract class BaseWireMockStubStrategy { private static Closure transform = { it instanceof DslProperty ? transformValues(it.clientValue, transform) : it diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockRequestStubStrategy.groovy similarity index 98% rename from accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy rename to accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockRequestStubStrategy.groovy index a98088b672..d0bb325bb1 100755 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockRequestStubStrategy.groovy @@ -21,11 +21,11 @@ import static io.codearte.accurest.util.RegexpBuilders.buildJSONRegexpMatch @TypeChecked @PackageScope -class WiremockRequestStubStrategy extends BaseWiremockStubStrategy { +class WireMockRequestStubStrategy extends BaseWireMockStubStrategy { private final Request request - WiremockRequestStubStrategy(GroovyDsl groovyDsl) { + WireMockRequestStubStrategy(GroovyDsl groovyDsl) { this.request = groovyDsl.request } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockResponseStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockResponseStubStrategy.groovy similarity index 92% rename from accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockResponseStubStrategy.groovy rename to accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockResponseStubStrategy.groovy index a17dad9420..3cb9a58155 100755 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockResponseStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockResponseStubStrategy.groovy @@ -11,12 +11,12 @@ import static io.codearte.accurest.util.ContentUtils.recognizeContentTypeFromHea @TypeChecked @PackageScope -class WiremockResponseStubStrategy extends BaseWiremockStubStrategy { +class WireMockResponseStubStrategy extends BaseWireMockStubStrategy { private final Request request private final Response response - WiremockResponseStubStrategy(GroovyDsl groovyDsl) { + WireMockResponseStubStrategy(GroovyDsl groovyDsl) { this.response = groovyDsl.response this.request = groovyDsl.request } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockStubStrategy.groovy new file mode 100644 index 0000000000..1793b8e61c --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockStubStrategy.groovy @@ -0,0 +1,21 @@ +package io.codearte.accurest.dsl + +import groovy.json.JsonOutput +import groovy.transform.CompileStatic + +@CompileStatic +class WireMockStubStrategy { + + private final WireMockRequestStubStrategy wireMockRequestStubStrategy + private final WireMockResponseStubStrategy wireMockResponseStubStrategy + + WireMockStubStrategy(GroovyDsl groovyDsl) { + this.wireMockRequestStubStrategy = new WireMockRequestStubStrategy(groovyDsl) + this.wireMockResponseStubStrategy = new WireMockResponseStubStrategy(groovyDsl) + } + + String toWireMockClientStub() { + return JsonOutput.prettyPrint(JsonOutput.toJson([request : wireMockRequestStubStrategy.buildClientRequestContent(), + response: wireMockResponseStubStrategy.buildClientResponseContent()])) + } +} diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockStubStrategy.groovy deleted file mode 100644 index a4bf3ee9c4..0000000000 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockStubStrategy.groovy +++ /dev/null @@ -1,21 +0,0 @@ -package io.codearte.accurest.dsl - -import groovy.json.JsonOutput -import groovy.transform.CompileStatic - -@CompileStatic -class WiremockStubStrategy { - - private final WiremockRequestStubStrategy wiremockRequestStubStrategy - private final WiremockResponseStubStrategy wiremockResponseStubStrategy - - WiremockStubStrategy(GroovyDsl groovyDsl) { - this.wiremockRequestStubStrategy = new WiremockRequestStubStrategy(groovyDsl) - this.wiremockResponseStubStrategy = new WiremockResponseStubStrategy(groovyDsl) - } - - String toWiremockClientStub() { - return JsonOutput.prettyPrint(JsonOutput.toJson([request : wiremockRequestStubStrategy.buildClientRequestContent(), - response: wiremockResponseStubStrategy.buildClientResponseContent()])) - } -} diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslResponseSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslResponseSpec.groovy similarity index 82% rename from accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslResponseSpec.groovy rename to accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslResponseSpec.groovy index 13299ceebe..07eb9aebda 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslResponseSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslResponseSpec.groovy @@ -3,7 +3,7 @@ package io.codearte.accurest.dsl import groovy.json.JsonSlurper import spock.lang.Specification -class WiremockGroovyDslResponseSpec extends Specification { +class WireMockGroovyDslResponseSpec extends Specification { def 'should generate response without body for client side'() { given: @@ -13,7 +13,7 @@ class WiremockGroovyDslResponseSpec extends Specification { } } expect: - new WiremockResponseStubStrategy(dsl).buildClientResponseContent() == new JsonSlurper().parseText(expectedStub) + new WireMockResponseStubStrategy(dsl).buildClientResponseContent() == new JsonSlurper().parseText(expectedStub) where: expectedStub << [''' { @@ -39,7 +39,7 @@ class WiremockGroovyDslResponseSpec extends Specification { } } expect: - new WiremockResponseStubStrategy(dsl).buildClientResponseContent() == new JsonSlurper().parseText(''' + new WireMockResponseStubStrategy(dsl).buildClientResponseContent() == new JsonSlurper().parseText(''' { "headers": { "Content-Type": "text/xml" diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy similarity index 87% rename from accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy rename to accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy index ca68489624..e7e6886b5d 100755 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy @@ -4,9 +4,9 @@ import groovy.json.JsonBuilder import groovy.json.JsonSlurper import spock.lang.Issue -class WiremockGroovyDslSpec extends WiremockSpec { +class WireMockGroovyDslSpec extends WireMockSpec { - def 'should convert groovy dsl stub to wiremock stub for the client side'() { + def 'should convert groovy dsl stub to wireMock stub for the client side'() { given: GroovyDsl groovyDsl = GroovyDsl.make { request { @@ -33,9 +33,9 @@ class WiremockGroovyDslSpec extends WiremockSpec { } } when: - String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockClientStub() + String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() then: - new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText(''' + new JsonSlurper().parseText(wireMockStub) == new JsonSlurper().parseText(''' { "request": { "method": "GET", @@ -51,11 +51,11 @@ class WiremockGroovyDslSpec extends WiremockSpec { } ''') and: - stubMappingIsValidWiremockStub(wiremockStub) + stubMappingIsValidWireMockStub(wireMockStub) } @Issue("#79") - def 'should convert groovy dsl stub to wiremock stub for the client side with a body containing a map'() { + def 'should convert groovy dsl stub to wireMock stub for the client side with a body containing a map'() { given: GroovyDsl groovyDsl = GroovyDsl.make { request { @@ -78,9 +78,9 @@ class WiremockGroovyDslSpec extends WiremockSpec { } } when: - String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockClientStub() + String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() then: - new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText(''' + new JsonSlurper().parseText(wireMockStub) == new JsonSlurper().parseText(''' { "request": { "method": "GET", @@ -98,7 +98,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { } ''') and: - stubMappingIsValidWiremockStub(wiremockStub) + stubMappingIsValidWireMockStub(wireMockStub) } @Issue("#86") @@ -122,9 +122,9 @@ class WiremockGroovyDslSpec extends WiremockSpec { } } when: - String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockClientStub() + String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() then: - new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText(''' + new JsonSlurper().parseText(wireMockStub) == new JsonSlurper().parseText(''' { "request": { "method": "POST", @@ -147,10 +147,10 @@ class WiremockGroovyDslSpec extends WiremockSpec { } ''') and: - stubMappingIsValidWiremockStub(wiremockStub) + stubMappingIsValidWireMockStub(wireMockStub) } - def 'should convert groovy dsl stub with Body as String to wiremock stub for the client side'() { + def 'should convert groovy dsl stub with Body as String to wireMock stub for the client side'() { given: GroovyDsl groovyDsl = GroovyDsl.make { request { @@ -174,9 +174,9 @@ class WiremockGroovyDslSpec extends WiremockSpec { } } when: - String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockClientStub() + String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() then: - new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText(''' + new JsonSlurper().parseText(wireMockStub) == new JsonSlurper().parseText(''' { "request": { "method": "GET", @@ -192,10 +192,10 @@ class WiremockGroovyDslSpec extends WiremockSpec { } ''') and: - stubMappingIsValidWiremockStub(wiremockStub) + stubMappingIsValidWireMockStub(wireMockStub) } - def 'should convert groovy dsl stub with simple Body as String to wiremock stub for the client side'() { + def 'should convert groovy dsl stub with simple Body as String to wireMock stub for the client side'() { given: GroovyDsl groovyDsl = GroovyDsl.make { request { @@ -221,9 +221,9 @@ class WiremockGroovyDslSpec extends WiremockSpec { } } when: - String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockClientStub() + String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() then: - new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText(''' + new JsonSlurper().parseText(wireMockStub) == new JsonSlurper().parseText(''' { "request": { "method": "GET", @@ -244,7 +244,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { } ''') and: - stubMappingIsValidWiremockStub(wiremockStub) + stubMappingIsValidWireMockStub(wireMockStub) } def 'should use equalToJson when body match is defined as map'() { @@ -271,9 +271,9 @@ class WiremockGroovyDslSpec extends WiremockSpec { } } when: - String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockClientStub() + String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() then: - new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText(''' + new JsonSlurper().parseText(wireMockStub) == new JsonSlurper().parseText(''' { "request": { "method": "GET", @@ -290,7 +290,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { } ''') and: - stubMappingIsValidWiremockStub(wiremockStub) + stubMappingIsValidWireMockStub(wireMockStub) } def 'should use equalToJson when content type ends with json'() { @@ -313,7 +313,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { } } when: - String json = toWiremockClientJsonStub(groovyDsl) + String json = toWireMockClientJsonStub(groovyDsl) then: parseJson(json) == parseJson(''' { @@ -337,7 +337,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { } ''') and: - stubMappingIsValidWiremockStub(json) + stubMappingIsValidWireMockStub(json) } def 'should use equalToXml when content type ends with xml'() { @@ -356,7 +356,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { } } when: - String json = toWiremockClientJsonStub(groovyDsl) + String json = toWireMockClientJsonStub(groovyDsl) then: parseJson(json) == parseJson(''' { @@ -380,7 +380,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { } ''') and: - stubMappingIsValidWiremockStub(json) + stubMappingIsValidWireMockStub(json) } def 'should use equalToXml when content type is parsable xml'() { @@ -396,7 +396,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { } } when: - String json = toWiremockClientJsonStub(groovyDsl) + String json = toWireMockClientJsonStub(groovyDsl) then: parseJson(json) == parseJson(''' { @@ -415,7 +415,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { } ''') and: - stubMappingIsValidWiremockStub(json) + stubMappingIsValidWireMockStub(json) } def 'should support xml as a response body'() { @@ -431,7 +431,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { } } when: - String json = toWiremockClientJsonStub(groovyDsl) + String json = toWireMockClientJsonStub(groovyDsl) then: parseJson(json) == parseJson(''' { @@ -446,7 +446,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { } ''') and: - stubMappingIsValidWiremockStub(json) + stubMappingIsValidWireMockStub(json) } def 'should use equalToJson'() { @@ -462,7 +462,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { } } when: - String json = toWiremockClientJsonStub(groovyDsl) + String json = toWireMockClientJsonStub(groovyDsl) then: parseJson(json) == parseJson(''' { @@ -481,7 +481,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { } ''') and: - stubMappingIsValidWiremockStub(json) + stubMappingIsValidWireMockStub(json) } def 'should use equalToXml'() { @@ -497,7 +497,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { } } when: - String json = toWiremockClientJsonStub(groovyDsl) + String json = toWireMockClientJsonStub(groovyDsl) then: parseJson(json) == parseJson(''' { @@ -516,10 +516,10 @@ class WiremockGroovyDslSpec extends WiremockSpec { } ''') and: - stubMappingIsValidWiremockStub(json) + stubMappingIsValidWireMockStub(json) } - def 'should convert groovy dsl stub with regexp Body as String to wiremock stub for the client side'() { + def 'should convert groovy dsl stub with regexp Body as String to wireMock stub for the client side'() { given: GroovyDsl groovyDsl = GroovyDsl.make { request { @@ -545,9 +545,9 @@ class WiremockGroovyDslSpec extends WiremockSpec { } } when: - String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockClientStub() + String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() then: - new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText(''' + new JsonSlurper().parseText(wireMockStub) == new JsonSlurper().parseText(''' { "request": { "method": "GET", @@ -566,7 +566,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { } ''') and: - stubMappingIsValidWiremockStub(wiremockStub) + stubMappingIsValidWireMockStub(wireMockStub) } def 'should convert groovy dsl stub with a regexp and an integer in request body'() { @@ -600,9 +600,9 @@ class WiremockGroovyDslSpec extends WiremockSpec { } when: - String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockClientStub() + String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() then: - new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText(''' + new JsonSlurper().parseText(wireMockStub) == new JsonSlurper().parseText(''' { "request": { "method": "PUT", @@ -626,7 +626,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { } ''') and: - stubMappingIsValidWiremockStub(wiremockStub) + stubMappingIsValidWireMockStub(wireMockStub) } @@ -638,7 +638,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { } } expect: - new WiremockRequestStubStrategy(groovyDsl).buildClientRequestContent() == new JsonSlurper().parseText(''' + new WireMockRequestStubStrategy(groovyDsl).buildClientRequestContent() == new JsonSlurper().parseText(''' { "method":"GET" } @@ -654,7 +654,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { } } expect: - new WiremockRequestStubStrategy(groovyDsl).buildClientRequestContent() == new JsonSlurper().parseText(''' + new WireMockRequestStubStrategy(groovyDsl).buildClientRequestContent() == new JsonSlurper().parseText(''' { "method":"GET", "url":"/sth" @@ -673,7 +673,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { } } expect: - new WiremockRequestStubStrategy(groovyDsl).buildClientRequestContent() == new JsonSlurper().parseText(''' + new WireMockRequestStubStrategy(groovyDsl).buildClientRequestContent() == new JsonSlurper().parseText(''' { "urlPattern":"^/[0-9]{2}$" } @@ -703,7 +703,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { } } when: - def json = toWiremockClientJsonStub(groovyDsl) + def json = toWireMockClientJsonStub(groovyDsl) then: parseJson(json) == parseJson(''' { @@ -743,7 +743,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { } ''') and: - stubMappingIsValidWiremockStub(json) + stubMappingIsValidWireMockStub(json) } def "should generate request with urlPath for client side"() { @@ -758,7 +758,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { } } when: - def json = toWiremockClientJsonStub(groovyDsl) + def json = toWireMockClientJsonStub(groovyDsl) then: parseJson(json) == parseJson(''' { @@ -772,7 +772,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { } ''') and: - stubMappingIsValidWiremockStub(json) + stubMappingIsValidWireMockStub(json) } def "should generate simple request with urlPath for client side"() { @@ -787,7 +787,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { } } when: - def json = toWiremockClientJsonStub(groovyDsl) + def json = toWireMockClientJsonStub(groovyDsl) then: parseJson(json) == parseJson(''' { @@ -801,7 +801,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { } ''') and: - stubMappingIsValidWiremockStub(json) + stubMappingIsValidWireMockStub(json) } def "should not allow regexp in url for server value"() { @@ -933,7 +933,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { } } when: - def json = toWiremockClientJsonStub(groovyDsl) + def json = toWireMockClientJsonStub(groovyDsl) then: parseJson(json) == parseJson(''' { @@ -955,7 +955,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { } ''') and: - stubMappingIsValidWiremockStub(json) + stubMappingIsValidWireMockStub(json) } def "should generate stub with some headers section for client side"() { @@ -976,7 +976,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { } } expect: - new WiremockRequestStubStrategy(groovyDsl).buildClientRequestContent() == new JsonSlurper().parseText(''' + new WireMockRequestStubStrategy(groovyDsl).buildClientRequestContent() == new JsonSlurper().parseText(''' { "headers": { "Content-Type": { @@ -993,7 +993,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { ''') } - def 'should convert groovy dsl stub with rich tree Body as String to wiremock stub for the client side'() { + def 'should convert groovy dsl stub with rich tree Body as String to wireMock stub for the client side'() { given: GroovyDsl groovyDsl = GroovyDsl.make { request { @@ -1032,9 +1032,9 @@ class WiremockGroovyDslSpec extends WiremockSpec { } } when: - String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockClientStub() + String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() then: - new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText(''' + new JsonSlurper().parseText(wireMockStub) == new JsonSlurper().parseText(''' { "request": { "method": "GET", @@ -1083,7 +1083,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { } } when: - def json = toWiremockClientJsonStub(groovyDsl) + def json = toWireMockClientJsonStub(groovyDsl) then: parseJson(json) == parseJson(''' { @@ -1115,7 +1115,7 @@ class WiremockGroovyDslSpec extends WiremockSpec { new JsonSlurper().parseText(json) } - String toWiremockClientJsonStub(groovyDsl) { - new WiremockStubStrategy(groovyDsl).toWiremockClientStub() + String toWireMockClientJsonStub(groovyDsl) { + new WireMockStubStrategy(groovyDsl).toWireMockClientStub() } } diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockSpec.groovy similarity index 76% rename from accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockSpec.groovy rename to accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockSpec.groovy index ab1133c9ae..aad0fa879e 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockSpec.groovy @@ -5,9 +5,9 @@ import spock.lang.Specification import java.util.regex.Pattern -abstract class WiremockSpec extends Specification { +abstract class WireMockSpec extends Specification { - void stubMappingIsValidWiremockStub(String mappingDefinition) { + void stubMappingIsValidWireMockStub(String mappingDefinition) { StubMapping stubMapping = StubMapping.buildFrom(mappingDefinition) stubMapping.request.bodyPatterns.findAll { it.matches }.every { Pattern.compile(it.matches) diff --git a/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/AccurestGradlePlugin.groovy b/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/AccurestGradlePlugin.groovy index cee3f48357..dcec7b2f61 100644 --- a/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/AccurestGradlePlugin.groovy +++ b/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/AccurestGradlePlugin.groovy @@ -11,7 +11,7 @@ import org.gradle.api.Task class AccurestGradlePlugin implements Plugin { private static final String GENERATE_SERVER_TESTS_TASK_NAME = 'generateAccurest' - private static final String DSL_TO_WIREMOCK_CLIENT_TASK_NAME = 'generateWiremockClientStubs' + private static final String DSL_TO_WIREMOCK_CLIENT_TASK_NAME = 'generateWireMockClientStubs' private static final Class IDEA_PLUGIN_CLASS = org.gradle.plugins.ide.idea.IdeaPlugin private static final String GROUP_NAME = "Verification" @@ -27,7 +27,8 @@ class AccurestGradlePlugin implements Plugin { setConfigurationDefaults(extension) createGenerateTestsTask(extension) - createAndConfigureGenerateWiremockClientStubsFromDslTask(extension) + createAndConfigureGenerateWireMockClientStubsFromDslTask(extension) + deprecatedCreateAndConfigureGenerateWiremockClientStubsFromDslTask() project.afterEvaluate { def hasIdea = project.plugins.findPlugin(IDEA_PLUGIN_CLASS) @@ -60,13 +61,21 @@ class AccurestGradlePlugin implements Plugin { } } - private void createAndConfigureGenerateWiremockClientStubsFromDslTask(AccurestConfigProperties extension) { - Task task = project.tasks.create(DSL_TO_WIREMOCK_CLIENT_TASK_NAME, GenerateWiremockClientStubsFromDslTask) - task.description = "Generate Wiremock client stubs from GroovyDSL" + private void createAndConfigureGenerateWireMockClientStubsFromDslTask(AccurestConfigProperties extension) { + Task task = project.tasks.create(DSL_TO_WIREMOCK_CLIENT_TASK_NAME, GenerateWireMockClientStubsFromDslTask) + task.description = "Generate WireMock client stubs from GroovyDSL" task.group = GROUP_NAME task.conventionMapping.with { contractsDslDir = { extension.contractsDslDir } stubsOutputDir = { extension.stubsOutputDir } } } + + private void deprecatedCreateAndConfigureGenerateWiremockClientStubsFromDslTask() { + Task task = project.tasks.create('generateWiremockClientStubs') + task.dependsOn('generateWireMockClientStubs') + task.description = "DEPRECATED - Generates WireMock client stubs. - DEPRECATED - use 'generateWireMockClientStubs' task" + task.group = GROUP_NAME + task.doFirst {logger.warn("DEPRECATION WARNING. Task 'generateWiremockClientStubs' is deprecated. Use 'generateWireMockClientStubs' task instead.")} + } } diff --git a/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/GenerateWiremockClientStubsFromDslTask.groovy b/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/GenerateWireMockClientStubsFromDslTask.groovy similarity index 74% rename from accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/GenerateWiremockClientStubsFromDslTask.groovy rename to accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/GenerateWireMockClientStubsFromDslTask.groovy index ab6f6473c1..be22c44def 100644 --- a/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/GenerateWiremockClientStubsFromDslTask.groovy +++ b/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/GenerateWireMockClientStubsFromDslTask.groovy @@ -1,6 +1,6 @@ package io.codearte.accurest.plugin -import io.codearte.accurest.wiremock.DslToWiremockClientConverter +import io.codearte.accurest.wiremock.DslToWireMockClientConverter import io.codearte.accurest.wiremock.RecursiveFilesConverter import org.gradle.api.internal.ConventionTask import org.gradle.api.tasks.InputDirectory @@ -8,7 +8,7 @@ import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction //TODO: Implement as an incremental task: https://gradle.org/docs/current/userguide/custom_tasks.html#incremental_tasks ? -class GenerateWiremockClientStubsFromDslTask extends ConventionTask { +class GenerateWireMockClientStubsFromDslTask extends ConventionTask { @InputDirectory File contractsDslDir @@ -17,10 +17,10 @@ class GenerateWiremockClientStubsFromDslTask extends ConventionTask { @TaskAction void generate() { - logger.info("Accurest Plugin: Invoking GroovyDSL to Wiremock client stubs conversion") + logger.info("Accurest Plugin: Invoking GroovyDSL to WireMock client stubs conversion") logger.debug("From '${getContractsDslDir()}' to '${getStubsOutputDir()}'") - RecursiveFilesConverter converter = new RecursiveFilesConverter(new DslToWiremockClientConverter(), getContractsDslDir(), + RecursiveFilesConverter converter = new RecursiveFilesConverter(new DslToWireMockClientConverter(), getContractsDslDir(), getStubsOutputDir()) converter.processFiles() } diff --git a/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/BasicFunctionalSpec.groovy b/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/BasicFunctionalSpec.groovy index 96cabca538..d488f03ff4 100755 --- a/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/BasicFunctionalSpec.groovy +++ b/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/BasicFunctionalSpec.groovy @@ -23,7 +23,7 @@ class BasicFunctionalSpec extends IntegrationSpec { when: def result = runTasksSuccessfully('check') then: - result.wasExecuted(":generateWiremockClientStubs") + result.wasExecuted(":generateWireMockClientStubs") result.wasExecuted(":generateAccurest") and: "tests generated" @@ -38,7 +38,7 @@ class BasicFunctionalSpec extends IntegrationSpec { def "should generate valid client json stubs for simple input"() { when: - runTasksSuccessfully('generateWiremockClientStubs') + runTasksSuccessfully('generateWireMockClientStubs') then: def generatedClientJsonStub = file(GENERATED_CLIENT_JSON_STUB).text new JsonSlurper().parseText(generatedClientJsonStub) == new JsonSlurper().parseText(""" @@ -67,16 +67,16 @@ class BasicFunctionalSpec extends IntegrationSpec { assert !fileExists(GENERATED_CLIENT_JSON_STUB) assert !fileExists(TEST_EXECUTION_XML_REPORT) when: - runTasksSuccessfully('generateWiremockClientStubs', 'generateAccurest') + runTasksSuccessfully('generateWireMockClientStubs', 'generateAccurest') then: fileExists(GENERATED_CLIENT_JSON_STUB) fileExists(GENERATED_TEST) when: "running generation without change inputs" - def secondExecutionResult = runTasksSuccessfully('generateWiremockClientStubs', 'generateAccurest') + def secondExecutionResult = runTasksSuccessfully('generateWireMockClientStubs', 'generateAccurest') then: "tasks should be up-to-date" - secondExecutionResult.wasUpToDate(":generateWiremockClientStubs") + secondExecutionResult.wasUpToDate(":generateWireMockClientStubs") secondExecutionResult.wasUpToDate(":generateAccurest") when: "inputs changed" @@ -84,10 +84,10 @@ class BasicFunctionalSpec extends IntegrationSpec { groovyDslFile.text = groovyDslFile.text.replace("200", "599") and: "tasks run" - def thirdExecutionResult = runTasksSuccessfully('generateWiremockClientStubs', 'generateAccurest') + def thirdExecutionResult = runTasksSuccessfully('generateWireMockClientStubs', 'generateAccurest') then: "tasks should be reexecuted" - thirdExecutionResult.wasExecuted(":generateWiremockClientStubs") + thirdExecutionResult.wasExecuted(":generateWireMockClientStubs") thirdExecutionResult.wasExecuted(":generateAccurest") and: "changes visible in generate files" diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/build.gradle b/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/build.gradle index 1241194253..ee5c91b466 100644 --- a/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/build.gradle +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/build.gradle @@ -12,8 +12,8 @@ apply plugin: 'accurest' ext { contractsDir = file("${project.rootDir}/repository/mappings/com/ofg/twitter-places-analyzer") - wiremockStubsOutputDirRoot = file("${project.buildDir}/production/${project.name}-stubs/") - wiremockStubsOutputDir = new File(wiremockStubsOutputDirRoot, 'repository/mappings/') + wireMockStubsOutputDirRoot = file("${project.buildDir}/production/${project.name}-stubs/") + wireMockStubsOutputDir = new File(wireMockStubsOutputDirRoot, 'repository/mappings/') } configurations { @@ -59,16 +59,16 @@ accurest { basePackageForTests = 'accurest' contractsDslDir = contractsDir // generatedTestSourcesDir = file("${project.rootDir}/src/test/groovy/") - stubsOutputDir = wiremockStubsOutputDir + stubsOutputDir = wireMockStubsOutputDir } //TODO: Put it into the plugin -task createWiremockStubsOutputDir << { - wiremockStubsOutputDir.mkdirs() +task createWireMockStubsOutputDir << { + wireMockStubsOutputDir.mkdirs() } -generateWiremockClientStubs.dependsOn { createWiremockStubsOutputDir } -generateAccurest.dependsOn generateWiremockClientStubs +generateWireMockClientStubs.dependsOn { createWireMockStubsOutputDir } +generateAccurest.dependsOn generateWireMockClientStubs wrapper { gradleVersion '2.2.1' diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/build.gradle b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/build.gradle index 9bf00d6c55..90478ca0bd 100644 --- a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/build.gradle +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/build.gradle @@ -40,8 +40,8 @@ configure([project(':fraudDetectionService'), project(':loanApplicationService') apply plugin: 'accurest' ext { - wiremockStubsOutputDirRoot = file("${project.buildDir}/production/${project.name}-stubs/") - wiremockStubsOutputDir = new File(wiremockStubsOutputDirRoot, 'mappings/') + wireMockStubsOutputDirRoot = file("${project.buildDir}/production/${project.name}-stubs/") + wireMockStubsOutputDir = new File(wireMockStubsOutputDirRoot, 'mappings/') } accurest { @@ -50,7 +50,7 @@ configure([project(':fraudDetectionService'), project(':loanApplicationService') baseClassForTests = 'com.blogspot.toomuchcoding.MvcSpec' contractsDslDir = file("${project.projectDir.absolutePath}/mappings/") generatedTestSourcesDir = file("${project.buildDir}/generated-sources/") - stubsOutputDir = wiremockStubsOutputDir + stubsOutputDir = wireMockStubsOutputDir } jar { @@ -79,7 +79,7 @@ configure([project(':fraudDetectionService'), project(':loanApplicationService') } configure(project(':fraudDetectionService')) { - test.dependsOn('generateWiremockClientStubs') + test.dependsOn('generateWireMockClientStubs') } configure(project(':loanApplicationService')) { @@ -92,3 +92,4 @@ configure(project(':loanApplicationService')) { generateAccurest.dependsOn('copyCollaboratorStubs') } + diff --git a/build.gradle b/build.gradle index 3ae3d0e265..835037077f 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,7 @@ buildscript { repositories { mavenCentral() + mavenLocal() } dependencies { classpath "pl.allegro.tech.build:axion-release-plugin:1.2.2" From 07a74c2448b6ca90983ad36cb5c1544f0ad8e2cd Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Thu, 25 Jun 2015 17:47:11 +0200 Subject: [PATCH 051/119] Release version: 0.7.0 [ci skip] From 0d4644b96f32d09ca5b90ad4a697a7975974ccbe Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Mon, 6 Jul 2015 13:21:44 +0200 Subject: [PATCH 052/119] Fixed failure on empty String in the body. Added tests for this use case. --- .../accurest/util/ContentUtils.groovy | 6 + .../builder/SpockMethodBuilderSpec.groovy | 21 +- .../accurest/dsl/WireMockGroovyDslSpec.groovy | 207 +++++++++++------- 3 files changed, 152 insertions(+), 82 deletions(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy index 59b4d726ee..b42cadcf5c 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy @@ -34,6 +34,9 @@ class ContentUtils { * @return JSON structure with replaced client / server side parts */ public static Object extractValue(GString bodyAsValue, ContentType contentType, Closure valueProvider) { + if (bodyAsValue.isEmpty()){ + return bodyAsValue + } if (contentType == ContentType.JSON) { return extractValueForJSON(bodyAsValue, valueProvider) } @@ -164,6 +167,9 @@ class ContentUtils { } public static boolean isJsonType(GString gstring) { + if (gstring.isEmpty()) { + return false + } GString stringWithoutValues = new GStringImpl( gstring.values.collect({ it instanceof String || it instanceof GString ? it.toString() : escapeJson(it.toString()) diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy index 964f03811b..49c1d94981 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy @@ -286,5 +286,24 @@ class SpockMethodBuilderSpec extends Specification { spockTest.contains('responseBody.property2 == "b"') } - + def "should generate test for empty body"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method('POST') + url("/ws/payments") + body("") + } + response { + status 406 + } + } + SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + def spockTest = blockBuilder.toString() + then: + spockTest.contains(".body('')") + } } diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy index e7e6886b5d..12aca1f271 100755 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy @@ -111,7 +111,11 @@ class WireMockGroovyDslSpec extends WireMockSpec { headers { header("Content-Type": 'application/x-www-form-urlencoded') } - body("""paymentType=INCOMING&transferType=BANK&amount=${value(client(regex('[0-9]{3}\\.[0-9]{2}')), server(500.00))}&bookingDate=${value(client(regex('[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])')), server('2015-05-18'))}""") + body("""paymentType=INCOMING&transferType=BANK&amount=${ + value(client(regex('[0-9]{3}\\.[0-9]{2}')), server(500.00)) + }&bookingDate=${ + value(client(regex('[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])')), server('2015-05-18')) + }""") } response { status 204 @@ -290,7 +294,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { } ''') and: - stubMappingIsValidWireMockStub(wireMockStub) + stubMappingIsValidWireMockStub(wireMockStub) } def 'should use equalToJson when content type ends with json'() { @@ -349,7 +353,9 @@ class WireMockGroovyDslSpec extends WireMockSpec { headers { header "Content-Type", "customtype/xml" } - body """${value(client('Jozo'), server('Denis'))}${value(client(""), server('1234567890'))}""" + body """${value(client('Jozo'), server('Denis'))}${ + value(client(""), server('1234567890')) + }""" } response { status 200 @@ -389,7 +395,9 @@ class WireMockGroovyDslSpec extends WireMockSpec { request { method 'GET' url "/users" - body """${value(client('Jozo'), server('Denis'))}${value(client(""), server('1234567890'))}""" + body """${value(client('Jozo'), server('Denis'))}${ + value(client(""), server('1234567890')) + }""" } response { status 200 @@ -427,7 +435,9 @@ class WireMockGroovyDslSpec extends WireMockSpec { } response { status 200 - body """${value(client('Jozo'), server('Denis'))}${value(client(""), server('1234567890'))}""" + body """${value(client('Jozo'), server('Denis'))}${ + value(client(""), server('1234567890')) + }""" } } when: @@ -490,7 +500,9 @@ class WireMockGroovyDslSpec extends WireMockSpec { request { method 'GET' url "/users" - body equalToXml("""${value(client('Jozo'), server('Denis'))}${value(client(""), server('1234567890'))}""") + body equalToXml("""${value(client('Jozo'), server('Denis'))}${ + value(client(""), server('1234567890')) + }""") } response { status 200 @@ -522,28 +534,28 @@ class WireMockGroovyDslSpec extends WireMockSpec { def 'should convert groovy dsl stub with regexp Body as String to wireMock stub for the client side'() { given: GroovyDsl groovyDsl = GroovyDsl.make { - request { - method('GET') - url $(client(regex('/[0-9]{2}')), server('/12')) - body """ + request { + method('GET') + url $(client(regex('/[0-9]{2}')), server('/12')) + body """ { "personalId": "${value(client(regex('^[0-9]{11}$')), server('57593728525'))}" } """ - } - response { - status 200 - body("""\ + } + response { + status 200 + body("""\ { "name": "Jan" } """ - ) - headers { - header 'Content-Type': 'text/plain' + ) + headers { + header 'Content-Type': 'text/plain' + } } } - } when: String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() then: @@ -827,43 +839,43 @@ class WireMockGroovyDslSpec extends WireMockSpec { def "should not allow regexp in query parameter for server value"() { when: - GroovyDsl.make { - request { - method 'GET' - url("abc") { - queryParameters { - parameter 'age': $(client(notMatching("^\\w*\$")), server(regex(".*"))) - } - } - } - response { - status 200 - } - } + GroovyDsl.make { + request { + method 'GET' + url("abc") { + queryParameters { + parameter 'age': $(client(notMatching("^\\w*\$")), server(regex(".*"))) + } + } + } + response { + status 200 + } + } then: - def e = thrown(IllegalStateException) - e.message.contains "Query parameter 'age' can't be a pattern for the server side" + def e = thrown(IllegalStateException) + e.message.contains "Query parameter 'age' can't be a pattern for the server side" } def "should not allow query parameter unresolvable for a server value"() { when: - GroovyDsl.make { - request { - method 'GET' - urlPath("users") { - queryParameters { - parameter 'age': notMatching("^\\w*\$") - parameter 'name': matching("Denis.*") - } - } - } - response { - status 200 - } - } + GroovyDsl.make { + request { + method 'GET' + urlPath("users") { + queryParameters { + parameter 'age': notMatching("^\\w*\$") + parameter 'name': matching("Denis.*") + } + } + } + response { + status 200 + } + } then: - def e = thrown(IllegalStateException) - e.message.contains "Query parameter 'age' can't be of a matching type: NOT_MATCHING for the server side" + def e = thrown(IllegalStateException) + e.message.contains "Query parameter 'age' can't be of a matching type: NOT_MATCHING for the server side" } def "should not allow query parameter with a different absent variation for server/client"() { @@ -874,45 +886,45 @@ class WireMockGroovyDslSpec extends WireMockSpec { e.message.contains "Absent cannot only be used only on one side" where: dsl << [ - { - request { - method 'GET' - urlPath("users") { - queryParameters { - parameter 'name': $(client(absent()), server("")) + { + request { + method 'GET' + urlPath("users") { + queryParameters { + parameter 'name': $(client(absent()), server("")) + } } } - } - response { - status 200 - } - }, - { - request { - method 'GET' - urlPath("users") { - queryParameters { - parameter 'name': $(client(""), server(absent())) + response { + status 200 + } + }, + { + request { + method 'GET' + urlPath("users") { + queryParameters { + parameter 'name': $(client(""), server(absent())) + } } } - } - response { - status 200 - } - }, - { - request { - method 'GET' - urlPath("users") { - queryParameters { - parameter 'name': $(client(absent()), server(matching("abc"))) + response { + status 200 + } + }, + { + request { + method 'GET' + urlPath("users") { + queryParameters { + parameter 'name': $(client(absent()), server(matching("abc"))) + } } } + response { + status 200 + } } - response { - status 200 - } - } ] } @@ -1107,6 +1119,39 @@ class WireMockGroovyDslSpec extends WireMockSpec { ''') } + def "should generate stub for empty body"() { + given: + GroovyDsl groovyDsl = GroovyDsl.make { + request { + method('POST') + url("test") + body("") + } + response { + status 406 + } + } + when: + def json = toWireMockClientJsonStub(groovyDsl) + then: + parseJson(json) == parseJson(''' + { + "request": { + "method": "POST", + "url": "test", + "bodyPatterns": [ + { + "equalTo": "" + } + ] + }, + "response": { + "status": 406 + } + } +''') + } + String toJsonString(value) { new JsonBuilder(value).toPrettyString() } From 45741b7ed5bebf8aa76d40a08ef0e74e801ad67c Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Mon, 6 Jul 2015 13:39:20 +0200 Subject: [PATCH 053/119] Have completely removed the accurest version as it is not required. --- .../src/test/resources/functionalTest/bootSimple/build.gradle | 4 ---- .../resources/functionalTest/bootSimple/gradle.properties | 1 - .../test/resources/functionalTest/sampleProject/build.gradle | 1 - 3 files changed, 6 deletions(-) diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/build.gradle b/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/build.gradle index ee5c91b466..48929c705b 100644 --- a/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/build.gradle +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/build.gradle @@ -2,9 +2,6 @@ buildscript { repositories { mavenCentral() } - dependencies { - classpath "io.codearte.accurest:accurest-gradle-plugin:$accurestVersion" - } } apply plugin: 'groovy' @@ -49,7 +46,6 @@ dependencies { testCompile "org.spockframework:spock-spring:0.7-groovy-2.0" testCompile "com.jayway.restassured:rest-assured:$restAssuredVersion" testCompile "com.jayway.restassured:spring-mock-mvc:$restAssuredVersion" - testCompile "io.codearte.accurest:accurest-core:$accurestVersion" testCompile "javax.servlet:javax.servlet-api:3.0.1" //provided testCompile "ch.qos.logback:logback-classic:1.1.2" } diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/gradle.properties b/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/gradle.properties index 6e02ac3599..71fcc0538e 100644 --- a/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/gradle.properties +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/gradle.properties @@ -1,5 +1,4 @@ groupId=com.ofg jacksonMapper=1.9.13 restAssuredVersion=2.4.0 -accurestVersion=0.4.1 springVersion=4.1.4.RELEASE \ No newline at end of file diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/build.gradle b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/build.gradle index 90478ca0bd..068c0f6e47 100644 --- a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/build.gradle +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/build.gradle @@ -5,7 +5,6 @@ buildscript { } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:1.2.1.RELEASE") - classpath 'io.codearte.accurest:accurest-gradle-plugin:0.6.2' } } From b5b6586984d9277006a38260772d8e5c5b727e0c Mon Sep 17 00:00:00 2001 From: Denis Stepanov Date: Wed, 15 Jul 2015 17:16:09 +0200 Subject: [PATCH 054/119] Add JAX-RS 2 Client API tests generation --- .../accurest/SingleTestGenerator.groovy | 7 +- .../JaxRsClientSpockMethodBodyBuilder.groovy | 95 +++++ .../accurest/builder/MethodBuilder.groovy | 26 +- .../MockMvcSpockMethodBodyBuilder.groovy | 77 ++++ .../builder/SpockMethodBodyBuilder.groovy | 252 ++++++------ .../codearte/accurest/config/TestMode.groovy | 2 +- .../codearte/accurest/util/ContentType.groovy | 12 +- .../JaxRsClientSpockMethodBuilderSpec.groovy | 362 ++++++++++++++++++ ...y => MockMvcSpockMethodBuilderSpec.groovy} | 24 +- .../plugin/SampleJerseyProjectSpec.groovy | 21 + .../sampleJerseyProject/build.gradle | 95 +++++ .../shouldMarkClientAsFraud.groovy | 27 ++ .../shouldMarkClientAsNotFraud.groovy | 28 ++ .../frauddetection/Application.java | 25 ++ .../FraudDetectionController.java | 38 ++ .../frauddetection/FraudRestApplication.java | 13 + .../frauddetection/model/FraudCheck.java | 29 ++ .../model/FraudCheckResult.java | 32 ++ .../model/FraudCheckStatus.java | 5 + .../src/main/resources/application.yml | 1 + .../com/blogspot/toomuchcoding/MvcSpec.groovy | 57 +++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 50514 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + .../sampleJerseyProject/gradlew | 164 ++++++++ .../sampleJerseyProject/gradlew.bat | 90 +++++ .../loanApplicationService/mappings/.gitkeep | 0 .../frauddetection/Application.java | 17 + .../LoanApplicationService.java | 62 +++ .../frauddetection/model/Client.java | 14 + .../model/FraudCheckStatus.java | 5 + .../model/FraudServiceRequest.java | 34 ++ .../model/FraudServiceResponse.java | 27 ++ .../frauddetection/model/LoanApplication.java | 36 ++ .../model/LoanApplicationResult.java | 32 ++ .../model/LoanApplicationStatus.java | 5 + .../src/main/resources/application.yml | 1 + .../LoanApplicationServiceSpec.groovy | 50 +++ .../shouldMarkClientAsFraud.json | 23 ++ .../shouldMarkClientAsNotFraud.json | 23 ++ .../sampleJerseyProject/settings.gradle | 2 + 40 files changed, 1672 insertions(+), 147 deletions(-) create mode 100644 accurest-core/src/main/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodBodyBuilder.groovy create mode 100644 accurest-core/src/main/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBodyBuilder.groovy create mode 100644 accurest-core/src/test/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodBuilderSpec.groovy rename accurest-core/src/test/groovy/io/codearte/accurest/builder/{SpockMethodBuilderSpec.groovy => MockMvcSpockMethodBuilderSpec.groovy} (87%) create mode 100755 accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/SampleJerseyProjectSpec.groovy create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/build.gradle create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsFraud.groovy create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.groovy create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/Application.java create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/FraudDetectionController.java create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/FraudRestApplication.java create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheck.java create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckResult.java create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckStatus.java create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/src/main/resources/application.yml create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/src/test/groovy/com/blogspot/toomuchcoding/MvcSpec.groovy create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradle/wrapper/gradle-wrapper.jar create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradle/wrapper/gradle-wrapper.properties create mode 100755 accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradlew create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradlew.bat create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/mappings/.gitkeep create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/Application.java create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/LoanApplicationService.java create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/Client.java create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckStatus.java create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudServiceRequest.java create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudServiceResponse.java create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplication.java create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplicationResult.java create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplicationStatus.java create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/resources/application.yml create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/test/groovy/com/blogspot/toomuchcoding/LoanApplicationServiceSpec.groovy create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsFraud.json create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.json create mode 100644 accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/settings.gradle diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/SingleTestGenerator.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/SingleTestGenerator.groovy index bc6f35adde..2765d7114b 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/SingleTestGenerator.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/SingleTestGenerator.groovy @@ -11,6 +11,7 @@ import static io.codearte.accurest.builder.MethodBuilder.createTestMethod import static io.codearte.accurest.util.NamesUtil.capitalize class SingleTestGenerator { + private final AccurestConfigProperties configProperties SingleTestGenerator(AccurestConfigProperties configProperties) { @@ -34,7 +35,9 @@ class SingleTestGenerator { } } - if (configProperties.testMode == TestMode.MOCKMVC) { + if (configProperties.testMode == TestMode.JAXRSCLIENT) { + clazz.addStaticImport('javax.ws.rs.client.Entity.*') + } else if (configProperties.testMode == TestMode.MOCKMVC) { clazz.addStaticImport('com.jayway.restassured.module.mockmvc.RestAssuredMockMvc.*') } else { clazz.addStaticImport('com.jayway.restassured.RestAssured.*') @@ -52,7 +55,7 @@ class SingleTestGenerator { } listOfFiles.each { - clazz.addMethod(createTestMethod(it, configProperties.targetFramework)) + clazz.addMethod(createTestMethod(it, configProperties)) } return clazz.build() } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodBodyBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodBodyBuilder.groovy new file mode 100644 index 0000000000..d3e07d7942 --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodBodyBuilder.groovy @@ -0,0 +1,95 @@ +package io.codearte.accurest.builder + +import groovy.transform.PackageScope +import groovy.transform.TypeChecked +import io.codearte.accurest.dsl.GroovyDsl +import io.codearte.accurest.dsl.internal.Header +import io.codearte.accurest.dsl.internal.QueryParameter + +@PackageScope +@TypeChecked +class JaxRsClientSpockMethodBodyBuilder extends SpockMethodBodyBuilder { + + JaxRsClientSpockMethodBodyBuilder(GroovyDsl stubDefinition) { + super(stubDefinition) + } + + @Override + protected void givenBlock(BlockBuilder bb) { + } + + @Override + protected void when(BlockBuilder bb) { + bb.addLine("def response = webTarget") + bb.indent() + + appendUrlPathAndQueryParameters(bb) + appendRequestWithRequiredResponseContentType(bb) + appendHeaders(bb) + appendMethodAndBody(bb) + + bb.unindent() + + bb.addEmptyLine() + bb.addLine("String responseAsString = response.readEntity(String)") + } + + protected void appendRequestWithRequiredResponseContentType(BlockBuilder bb) { + String acceptHeader = getHeader("Accept") + if (acceptHeader) { + bb.addLine(".request('$acceptHeader')") + } else { + bb.addLine(".request()") + } + } + + protected void appendUrlPathAndQueryParameters(BlockBuilder bb) { + if (request.url) { + bb.addLine(".path('$request.url.serverValue')") + } else if (request.urlPath) { + bb.addLine(".path('$request.urlPath.serverValue')") + request.urlPath.queryParameters?.parameters.findAll(this.&allowedQueryParameter).each { QueryParameter param -> + bb.addLine(".queryParam('$param.name', '${resolveParamValue(param).toString()}')") + } + } + } + + protected void appendMethodAndBody(BlockBuilder bb) { + String method = request.method.serverValue.toString().toLowerCase() + if (request.body) { + String contentType = getHeader('Content-Type') ?: getRequestContentType().mimeType + bb.addLine(".method('$method', entity('$bodyAsString', '$contentType'))") + } else { + bb.addLine(".method('$method')") + } + } + + protected appendHeaders(BlockBuilder bb) { + request.headers?.collect { Header header -> + if (header.name == 'Content-Type' || header.name == 'Accept') return // Particular headers are set via 'request' / 'entity' methods + bb.addLine(".header('${header.name}', '${header.serverValue}')") + } + } + + protected String getHeader(String name) { + return request.headers?.entries.find { it.name == name }?.serverValue + } + + @Override + protected void validateResponseCodeBlock(BlockBuilder bb) { + bb.addLine("response.status == $response.status.serverValue") + } + + @Override + protected void validateResponseHeadersBlock(BlockBuilder bb) { + response.headers?.collect { Header header -> + bb.addLine("response.getHeaderString('$header.name') == '$header.serverValue'") + } + } + + @Override + protected String getResponseAsString() { + return 'responseAsString' + } + +} diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/MethodBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/MethodBuilder.groovy index dd791fb666..c4e8da6d04 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/builder/MethodBuilder.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/MethodBuilder.groovy @@ -1,7 +1,9 @@ package io.codearte.accurest.builder import groovy.util.logging.Slf4j +import io.codearte.accurest.config.AccurestConfigProperties import io.codearte.accurest.config.TestFramework +import io.codearte.accurest.config.TestMode import io.codearte.accurest.dsl.GroovyDsl import io.codearte.accurest.util.NamesUtil @@ -13,28 +15,36 @@ class MethodBuilder { private final String methodName private final GroovyDsl stubContent - private final TestFramework lang + private final AccurestConfigProperties configProperties - private MethodBuilder(String methodName, GroovyDsl stubContent, TestFramework lang) { + private MethodBuilder(String methodName, GroovyDsl stubContent, AccurestConfigProperties configProperties) { this.stubContent = stubContent this.methodName = methodName - this.lang = lang + this.configProperties = configProperties } - static MethodBuilder createTestMethod(File stubsFile, TestFramework lang) { + static MethodBuilder createTestMethod(File stubsFile, AccurestConfigProperties configProperties) { log.debug("Stub content from file [${stubsFile.text}]") GroovyDsl stubContent = new GroovyShell(this.classLoader).evaluate(stubsFile) log.debug("Stub content Groovy DSL [$stubContent]") String methodName = NamesUtil.camelCase(NamesUtil.toLastDot(NamesUtil.afterLast(stubsFile.path, File.separator))) - return new MethodBuilder(methodName, stubContent, lang) + return new MethodBuilder(methodName, stubContent, configProperties) } void appendTo(BlockBuilder blockBuilder) { - if (lang == TestFramework.JUNIT) { + if (configProperties.targetFramework == TestFramework.JUNIT) { blockBuilder.addLine('@Test') } - blockBuilder.addLine(lang.methodModifier + "$methodName() {") - new SpockMethodBodyBuilder(stubContent).appendTo(blockBuilder) + blockBuilder.addLine(configProperties.targetFramework.methodModifier + "$methodName() {") + getMethodBodyBuilder().appendTo(blockBuilder) blockBuilder.addLine('}') } + + private SpockMethodBodyBuilder getMethodBodyBuilder() { + if (configProperties.testMode == TestMode.JAXRSCLIENT) { + return new JaxRsClientSpockMethodBodyBuilder(stubContent) + } + return new MockMvcSpockMethodBodyBuilder(stubContent) + } + } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBodyBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBodyBuilder.groovy new file mode 100644 index 0000000000..40d6a24416 --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBodyBuilder.groovy @@ -0,0 +1,77 @@ +package io.codearte.accurest.builder + +import groovy.transform.PackageScope +import groovy.transform.TypeChecked +import groovy.transform.TypeCheckingMode +import io.codearte.accurest.dsl.GroovyDsl +import io.codearte.accurest.dsl.internal.Header +import io.codearte.accurest.dsl.internal.QueryParameter +import io.codearte.accurest.dsl.internal.Request +import io.codearte.accurest.dsl.internal.UrlPath + +@PackageScope +@TypeChecked +class MockMvcSpockMethodBodyBuilder extends SpockMethodBodyBuilder { + + MockMvcSpockMethodBodyBuilder(GroovyDsl stubDefinition) { + super(stubDefinition) + } + + protected void given(BlockBuilder bb) { + bb.addLine('def request = given()') + bb.indent() + request.headers?.collect { Header header -> + bb.addLine(".header('${header.name}', '${header.serverValue}')") + } + if (request.body) { + bb.addLine(".body('$bodyAsString')") + } + bb.unindent() + } + + protected void when(BlockBuilder bb) { + bb.addLine('def response = given().spec(request)') + bb.indent() + + String url = buildUrl(request) + String method = request.method.serverValue.toString().toLowerCase() + + bb.addLine(/.${method}("$url")/) + bb.unindent() + } + + protected void validateResponseCodeBlock(BlockBuilder bb) { + bb.addLine("response.statusCode == $response.status.serverValue") + } + + protected void validateResponseHeadersBlock(BlockBuilder bb) { + response.headers?.collect { Header header -> + bb.addLine("response.header('$header.name') == '$header.serverValue'") + } + } + + @Override + protected String getResponseAsString() { + return 'response.body.asString()' + } + + protected String buildUrl(Request request) { + if (request.url) + return request.url.serverValue; + if (request.urlPath) + return buildUrlFromUrlPath(request.urlPath) + throw new IllegalStateException("URL is not set!") + } + + @TypeChecked(TypeCheckingMode.SKIP) + protected String buildUrlFromUrlPath(UrlPath urlPath) { + String params = urlPath.queryParameters.parameters + .findAll(this.&allowedQueryParameter) + .inject([] as List) { List result, QueryParameter param -> + result << "${param.name}=${resolveParamValue(param).toString()}" + } + .join('&') + return "$urlPath.serverValue?$params" + } + +} diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy index 7e7bb697bc..0101a64c9c 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy @@ -1,16 +1,14 @@ package io.codearte.accurest.builder - import groovy.json.JsonOutput import groovy.transform.PackageScope +import groovy.transform.TypeChecked import io.codearte.accurest.dsl.GroovyDsl import io.codearte.accurest.dsl.internal.DslProperty import io.codearte.accurest.dsl.internal.ExecutionProperty -import io.codearte.accurest.dsl.internal.Header import io.codearte.accurest.dsl.internal.MatchingStrategy import io.codearte.accurest.dsl.internal.QueryParameter import io.codearte.accurest.dsl.internal.Request import io.codearte.accurest.dsl.internal.Response -import io.codearte.accurest.dsl.internal.UrlPath import io.codearte.accurest.util.ContentType import io.codearte.accurest.util.JsonConverter @@ -24,85 +22,95 @@ import static io.codearte.accurest.util.ContentUtils.recognizeContentTypeFromHea * @author Jakub Kubrynski */ @PackageScope -class SpockMethodBodyBuilder { - private final GroovyDsl stubDefinition +@TypeChecked +abstract class SpockMethodBodyBuilder { + + protected final Request request + protected final Response response SpockMethodBodyBuilder(GroovyDsl stubDefinition) { - this.stubDefinition = stubDefinition + this.request = stubDefinition.request + this.response = stubDefinition.response } void appendTo(BlockBuilder blockBuilder) { - Request request = stubDefinition.request - Response response = stubDefinition.response - blockBuilder.with { - startBlock() - addLine('given:').startBlock() - addLine('def request = given()') - indent() - request.headers?.collect { Header header -> - addLine(".header('${header.name}', '${header.serverValue}')") - } - if (request.body) { - Object bodyValue = extractServerValueFromBody(request.body.serverValue) - String matches = trimRepeatedQuotes(new JsonOutput().toJson(bodyValue)) - addLine(".body('$matches')") - } + blockBuilder.startBlock() - unindent().endBlock().addEmptyLine() + givenBlock(blockBuilder) + whenBlock(blockBuilder) + thenBlock(blockBuilder) - addLine('when:').startBlock() - addLine('def response = given().spec(request)') - indent() + blockBuilder.endBlock() + } - String url = buildUrl(request) - String method = request.method.serverValue.toLowerCase() + protected void thenBlock(BlockBuilder bb) { + bb.addLine('then:') + bb.startBlock() + then(bb) + bb.endBlock() + } - blockBuilder.addLine(/.${method}("$url")/) - unindent().endBlock().addEmptyLine() + protected void whenBlock(BlockBuilder bb) { + bb.addLine('when:') + bb.startBlock() + when(bb) + bb.endBlock().addEmptyLine() + } - addLine('then:').startBlock() - addLine("response.statusCode == $response.status.serverValue") + protected void givenBlock(BlockBuilder bb) { + bb.addLine('given:') + bb.startBlock() + given(bb) + bb.endBlock().addEmptyLine() + } - response.headers?.collect { Header header -> - addLine("response.header('$header.name') == '$header.serverValue'") - } - if (response.body) { - endBlock() - addLine('and:').startBlock() - def responseBody = response.body.serverValue - ContentType contentType = recognizeContentTypeFromHeader(response.headers) - if (contentType == ContentType.UNKNOWN) { - contentType = recognizeContentTypeFromContent(responseBody) - } - if (responseBody instanceof GString) { - responseBody = extractValue(responseBody, contentType, { DslProperty dslProperty -> dslProperty.serverValue }) - } - if (contentType == ContentType.JSON) { - addLine('def responseBody = new JsonSlurper().parseText(response.body.asString())') - if (responseBody instanceof List) { - processArrayElements(responseBody, "", blockBuilder) - } else { - processMapElement(responseBody, blockBuilder, "") - } - } else if (contentType == ContentType.XML) { - addLine('def responseBody = new XmlSlurper().parseText(response.body.asString())') - // TODO xml validation - } - } - endBlock() + protected void given(BlockBuilder bb) {} - endBlock() + protected abstract void when(BlockBuilder bb) + + protected abstract void validateResponseCodeBlock(BlockBuilder bb) + + protected abstract void validateResponseHeadersBlock(BlockBuilder bb) + + protected abstract String getResponseAsString() + + protected void then(BlockBuilder bb) { + validateResponseCodeBlock(bb) + if (response.headers) { + validateResponseHeadersBlock(bb) + } + if (response.body) { + bb.endBlock() + bb.addLine('and:').startBlock() + validateResponseBodyBlock(bb) } } - private String trimRepeatedQuotes(String toTrim) { - if (toTrim.startsWith('"')) { - return toTrim.replaceAll('"', '') + protected void validateResponseBodyBlock(BlockBuilder bb) { + def responseBody = response.body.serverValue + ContentType contentType = getResponseContentType() + if (responseBody instanceof GString) { + responseBody = extractValue(responseBody, contentType, { DslProperty dslProperty -> dslProperty.serverValue }) + } + if (contentType == ContentType.JSON) { + bb.addLine("def responseBody = new JsonSlurper().parseText($responseAsString)") + processBodyElement(bb, "", responseBody) + } else if (contentType == ContentType.XML) { + bb.addLine("def responseBody = new XmlSlurper().parseText($responseAsString)") + // TODO xml validation } - return toTrim } - private Object extractServerValueFromBody(bodyValue) { + protected String getBodyAsString() { + Object bodyValue = extractServerValueFromBody(request.body.serverValue) + return trimRepeatedQuotes(new JsonOutput().toJson(bodyValue)) + } + + protected String trimRepeatedQuotes(String toTrim) { + return toTrim.startsWith('"') ? toTrim.replaceAll('"', '') : toTrim + } + + protected Object extractServerValueFromBody(bodyValue) { if (bodyValue instanceof GString) { bodyValue = extractValue(bodyValue, { DslProperty dslProperty -> dslProperty.serverValue }) } else { @@ -111,88 +119,86 @@ class SpockMethodBodyBuilder { return bodyValue } - private String buildUrl(Request request) { - if (request.url) - return request.url.serverValue; - if (request.urlPath) - return buildUrlFromUrlPath(request.urlPath) - throw new IllegalStateException("URL is not set!") - } - - private String buildUrlFromUrlPath(UrlPath urlPath) { - String params = urlPath.queryParameters.parameters - .findAll(this.&allowedQueryParameter) - .inject([]) { result, param -> - result << "${param.name}=${resolveParamValue(param).toString()}" - }.join('&') - return "$urlPath.serverValue?$params" - } - - private boolean allowedQueryParameter(QueryParameter param) { + protected boolean allowedQueryParameter(QueryParameter param) { return allowedQueryParameter(param.serverValue) } - private boolean allowedQueryParameter(MatchingStrategy matchingStrategy) { + protected boolean allowedQueryParameter(MatchingStrategy matchingStrategy) { return matchingStrategy.type != MatchingStrategy.Type.ABSENT } - private boolean allowedQueryParameter(Object o) { + protected boolean allowedQueryParameter(Object o) { return true } - private String resolveParamValue(QueryParameter param) { - resolveParamValue(param.serverValue) + protected String resolveParamValue(QueryParameter param) { + return resolveParamValue(param.serverValue) } - private String resolveParamValue(Object value) { - value.toString() + protected String resolveParamValue(Object value) { + return value.toString() } - private String resolveParamValue(MatchingStrategy matchingStrategy) { - matchingStrategy.serverValue.toString() + protected String resolveParamValue(MatchingStrategy matchingStrategy) { + return matchingStrategy.serverValue.toString() } - private void processBodyElement(BlockBuilder blockBuilder, String property, def value) { - if (value instanceof String) { - if (value.startsWith('$')) { - value = value.substring(1).replaceAll('\\$value', "responseBody$property") - blockBuilder.addLine(value) - } else { - blockBuilder.addLine("responseBody$property == \"${value}\"") - } - } else if (value instanceof Map) { - processMapElement(value, blockBuilder, property) - } else if (value instanceof Map.Entry) { - processEntryElement(blockBuilder, property, value) - } else if (value instanceof List) { - processArrayElements(value, property, blockBuilder) - } else if (value instanceof Pattern) { - blockBuilder.addLine("responseBody$property ==~ java.util.regex.Pattern.compile('${value}')") - } else if (value instanceof DslProperty) { - processBodyElement(blockBuilder, property, value.serverValue) - } else if (value instanceof ExecutionProperty) { - ExecutionProperty exec = (ExecutionProperty) value - blockBuilder.addLine("${exec.insertValue("responseBody$property")}") + protected void processBodyElement(BlockBuilder blockBuilder, String property, Object value) { + blockBuilder.addLine("responseBody$property == ${value}") + } + + protected void processBodyElement(BlockBuilder blockBuilder, String property, String value) { + if (value.startsWith('$')) { + value = value.substring(1).replaceAll('\\$value', "responseBody$property") + blockBuilder.addLine(value) } else { - blockBuilder.addLine("responseBody$property == ${value}") + blockBuilder.addLine("responseBody$property == \"${value}\"") } } - private void processMapElement(def value, BlockBuilder blockBuilder, String property) { - value.each { entry -> processEntryElement(blockBuilder, property, entry) } + protected void processBodyElement(BlockBuilder blockBuilder, String property, Pattern pattern) { + blockBuilder.addLine("responseBody$property ==~ java.util.regex.Pattern.compile('${pattern.pattern()}')") } - private def processEntryElement(BlockBuilder blockBuilder, String property, def entry) { - return processBodyElement(blockBuilder, property + "." + entry.key, entry.value) + protected void processBodyElement(BlockBuilder blockBuilder, String property, DslProperty dslProperty) { + processBodyElement(blockBuilder, property, dslProperty.serverValue) } - private void processArrayElements(List responseBody, String property, BlockBuilder blockBuilder) { - responseBody.eachWithIndex { - listElement, listIndex -> - listElement.each { entry -> - String prop = "$property[$listIndex]" ?: '' - processBodyElement(blockBuilder, prop, entry) - } + protected void processBodyElement(BlockBuilder blockBuilder, String property, ExecutionProperty exec) { + blockBuilder.addLine("${exec.insertValue("responseBody$property")}") + } + + protected void processBodyElement(BlockBuilder blockBuilder, String property, Map.Entry entry) { + processBodyElement(blockBuilder, property + "." + entry.key, entry.value) + } + + protected void processBodyElement(BlockBuilder blockBuilder, String property, Map map) { + map.each { + processBodyElement(blockBuilder, property, it) } } + + protected void processBodyElement(BlockBuilder blockBuilder, String property, List list) { + list.eachWithIndex { listElement, listIndex -> + String prop = "$property[$listIndex]" ?: '' + processBodyElement(blockBuilder, prop, listElement) + } + } + + protected ContentType getRequestContentType() { + ContentType contentType = recognizeContentTypeFromHeader(request.headers) + if (contentType == ContentType.UNKNOWN) { + contentType = recognizeContentTypeFromContent(request.body.serverValue) + } + return contentType + } + + protected ContentType getResponseContentType() { + ContentType contentType = recognizeContentTypeFromHeader(response.headers) + if (contentType == ContentType.UNKNOWN) { + contentType = recognizeContentTypeFromContent(response.body.serverValue) + } + return contentType + } + } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/config/TestMode.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/config/TestMode.groovy index e8a232e37c..1c7732d49e 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/config/TestMode.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/config/TestMode.groovy @@ -4,5 +4,5 @@ package io.codearte.accurest.config * @author Jakub Kubrynski */ enum TestMode { - MOCKMVC, EXPLICIT + MOCKMVC, EXPLICIT, JAXRSCLIENT } \ No newline at end of file diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentType.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentType.groovy index 142c93a945..7b9e402985 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentType.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentType.groovy @@ -1,5 +1,15 @@ package io.codearte.accurest.util enum ContentType { - JSON, XML, UNKNOWN + + JSON("application/json"), + XML("application/xml"), + UNKNOWN("application/octet-stream") + + final String mimeType + + ContentType(String mimeType) { + this.mimeType = mimeType + } + } \ No newline at end of file diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodBuilderSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodBuilderSpec.groovy new file mode 100644 index 0000000000..6b39c26e8d --- /dev/null +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodBuilderSpec.groovy @@ -0,0 +1,362 @@ +package io.codearte.accurest.builder + +import io.codearte.accurest.dsl.GroovyDsl +import spock.lang.Issue +import spock.lang.Specification + +class JaxRsClientSpockMethodBuilderSpec extends Specification { + + def "should generate assertions for simple response body"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "GET" + url "test" + } + response { + status 200 + body """{ + "property1": "a", + "property2": "b" +}""" + } + } + JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains("responseBody.property1 == \"a\"") + blockBuilder.toString().contains("responseBody.property2 == \"b\"") + } + + @Issue("#79") + def "should generate assertions for simple response body constructed from map with a list"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "GET" + url "test" + } + response { + status 200 + body( + property1: 'a', + property2: [ + [a: 'sth'], + [b: 'sthElse'] + ] + ) + } + } + JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains("responseBody.property1 == \"a\"") + blockBuilder.toString().contains("responseBody.property2[0].a == \"sth\"") + blockBuilder.toString().contains("responseBody.property2[1].b == \"sthElse\"") + } + + @Issue("#82") + def "should generate proper request when body constructed from map with a list"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "GET" + url "test" + body( + items: ['HOP'] + ) + } + response { + status 200 + } + } + JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains("entity('{\"items\":[\"HOP\"]}', 'application/json')") + } + + @Issue("#88") + def "should generate proper request when body constructed from GString"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "GET" + url "test" + body( + "property1=VAL1" + ) + } + response { + status 200 + } + } + JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains("entity('property1=VAL1', 'application/octet-stream')") + } + + def "should generate assertions for array in response body"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "GET" + url "test" + } + response { + status 200 + body """[ +{ + "property1": "a" +}, +{ + "property2": "b" +}]""" + } + } + JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains("responseBody[0].property1 == \"a\"") + blockBuilder.toString().contains("responseBody[1].property2 == \"b\"") + } + + def "should generate assertions for array inside response body element"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "GET" + url "test" + } + response { + status 200 + body """{ + "property1": [ + { "property2": "test1"}, + { "property3": "test2"} + ] +}""" + } + } + JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains("responseBody.property1[0].property2 == \"test1\"") + blockBuilder.toString().contains("responseBody.property1[1].property3 == \"test2\"") + } + + def "should generate assertions for nested objects in response body"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "GET" + url "test" + } + response { + status 200 + body '''\ +{ + "property1": "a", + "property2": {"property3": "b"} +} +''' + } + } + JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains("responseBody.property1 == \"a\"") + blockBuilder.toString().contains("responseBody.property2.property3 == \"b\"") + } + + def "should generate regex assertions for map objects in response body"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "GET" + url "test" + } + response { + status 200 + body( + property1: "a", + property2: value( + client('123'), + server(regex('[0-9]{3}')) + ) + ) + headers { + header('Content-Type': 'application/json') + + } + + } + } + JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains("responseBody.property1 == \"a\"") + blockBuilder.toString().contains("responseBody.property2 ==~ java.util.regex.Pattern.compile('[0-9]{3}')") + } + + def "should generate regex assertions for string objects in response body"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "GET" + url "test" + } + response { + status 200 + body("""{"property1":"a","property2":"${value(client('123'), server(regex('[0-9]{3}')))}"}""") + headers { + header('Content-Type': 'application/json') + + } + + } + } + JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains("responseBody.property1 == \"a\"") + blockBuilder.toString().contains("responseBody.property2 ==~ java.util.regex.Pattern.compile('[0-9]{3}')") + } + + def "should ignore 'Accept' header and use 'request' method"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "GET" + url "test" + headers { + header("Accept", "text/plain") + } + } + response { + status 200 + } + } + JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains("request('text/plain')") + } + + def "should ignore 'Content-Type' header and use 'entity' method"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "GET" + url "test" + headers { + header("Content-Type", "text/plain") + header("Timer", "123") + } + body '' + } + response { + status 200 + } + } + JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains("entity('', 'text/plain')") + blockBuilder.toString().contains("header('Timer', '123')") + !blockBuilder.toString().contains("header('Content-Type'") + + } + + def "should generate a call with an url path and query parameters"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method 'GET' + urlPath('/users') { + queryParameters { + parameter 'limit': $(client(equalTo("20")), server(equalTo("10"))) + parameter 'offset': $(client(containing("20")), server(equalTo("20"))) + parameter 'filter': "email" + parameter 'sort': equalTo("name") + parameter 'search': $(client(notMatching(~/^\/[0-9]{2}$/)), server("55")) + parameter 'age': $(client(notMatching("^\\w*\$")), server("99")) + parameter 'name': $(client(matching("Denis.*")), server("Denis.Stepanov")) + parameter 'email': "bob@email.com" + parameter 'hello': $(client(matching("Denis.*")), server(absent())) + parameter 'hello': absent() + } + } + } + response { + status 200 + body """ + { + "property1": "a", + "property2": "b" + } + """ + } + } + JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + def spockTest = blockBuilder.toString() + then: + spockTest.contains("queryParam('limit', '10'") + spockTest.contains("queryParam('offset', '20'") + spockTest.contains("queryParam('filter', 'email'") + spockTest.contains("queryParam('sort', 'name'") + spockTest.contains("queryParam('search', '55'") + spockTest.contains("queryParam('age', '99'") + spockTest.contains("queryParam('name', 'Denis.Stepanov'") + spockTest.contains("queryParam('email', 'bob@email.com'") + spockTest.contains('responseBody.property1 == "a"') + spockTest.contains('responseBody.property2 == "b"') + } + + def "should generate test for empty body"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method('POST') + url("/ws/payments") + body("") + } + response { + status 406 + } + } + JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + def spockTest = blockBuilder.toString() + then: + spockTest.contains("entity('', 'application/octet-stream')") + } +} diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy similarity index 87% rename from accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy rename to accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy index 49c1d94981..55a43d2961 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy @@ -7,7 +7,7 @@ import spock.lang.Specification /** * @author Jakub Kubrynski */ -class SpockMethodBuilderSpec extends Specification { +class MockMvcSpockMethodBuilderSpec extends Specification { def "should generate assertions for simple response body"() { given: @@ -24,7 +24,7 @@ class SpockMethodBuilderSpec extends Specification { }""" } } - SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) @@ -52,7 +52,7 @@ class SpockMethodBuilderSpec extends Specification { ) } } - SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) @@ -77,7 +77,7 @@ class SpockMethodBuilderSpec extends Specification { status 200 } } - SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) @@ -100,7 +100,7 @@ class SpockMethodBuilderSpec extends Specification { status 200 } } - SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) @@ -126,7 +126,7 @@ class SpockMethodBuilderSpec extends Specification { }]""" } } - SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) @@ -152,7 +152,7 @@ class SpockMethodBuilderSpec extends Specification { }""" } } - SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) @@ -178,7 +178,7 @@ class SpockMethodBuilderSpec extends Specification { ''' } } - SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) @@ -210,7 +210,7 @@ class SpockMethodBuilderSpec extends Specification { } } - SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) @@ -236,7 +236,7 @@ class SpockMethodBuilderSpec extends Specification { } } - SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) @@ -275,7 +275,7 @@ class SpockMethodBuilderSpec extends Specification { """ } } - SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) @@ -298,7 +298,7 @@ class SpockMethodBuilderSpec extends Specification { status 406 } } - SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl) + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) diff --git a/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/SampleJerseyProjectSpec.groovy b/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/SampleJerseyProjectSpec.groovy new file mode 100755 index 0000000000..789d897199 --- /dev/null +++ b/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/SampleJerseyProjectSpec.groovy @@ -0,0 +1,21 @@ +package io.codearte.accurest.plugin + +import nebula.test.IntegrationSpec +import spock.lang.Stepwise + +@Stepwise +class SampleJerseyProjectSpec extends IntegrationSpec { + + void setup() { + copyResources("functionalTest/sampleJerseyProject", "") + runTasksSuccessfully('clean') //delete accidental output when previously importing SimpleBoot into Idea to tweak it + } + + def "should pass basic flow"() { + given: + assert fileExists('build.gradle') + expect: + runTasksSuccessfully('check') + } + +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/build.gradle b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/build.gradle new file mode 100644 index 0000000000..0ae605c532 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/build.gradle @@ -0,0 +1,95 @@ +buildscript { + repositories { + mavenLocal() + mavenCentral() + } + dependencies { + classpath "org.springframework.boot:spring-boot-gradle-plugin:1.2.1.RELEASE" + } +} + +ext { + spockVersion = '0.7-groovy-2.0' + restAssuredVersion = '2.4.0' + + accurestStubsBaseDirectory = 'src/test/resources/stubs' +} + +subprojects { + apply plugin: 'groovy' + + + repositories { + mavenCentral() + mavenLocal() + } + + dependencies { + testCompile "org.codehaus.groovy:groovy-all:2.3.7" + testCompile "org.spockframework:spock-core:$spockVersion" + testCompile("junit:junit:4.12") + testCompile('com.github.tomakehurst:wiremock:1.52') { + exclude group: 'org.mortbay.jetty', module: 'servlet-api' + } + } +} + +configure([project(':fraudDetectionService'), project(':loanApplicationService')]) { + apply plugin: 'spring-boot' + apply plugin: 'accurest' + + ext { + wireMockStubsOutputDirRoot = file("${project.buildDir}/production/${project.name}-stubs/") + wireMockStubsOutputDir = new File(wireMockStubsOutputDirRoot, 'mappings/') + } + + accurest { + targetFramework = 'Spock' + testMode = 'JaxRsClient' + baseClassForTests = 'com.blogspot.toomuchcoding.MvcSpec' + contractsDslDir = file("${project.projectDir.absolutePath}/mappings/") + generatedTestSourcesDir = file("${project.buildDir}/generated-sources/") + stubsOutputDir = wireMockStubsOutputDir + } + + jar { + version = '0.0.1' + } + + dependencies { + compile "javax.ws.rs:javax.ws.rs-api:2.0.1" + compile 'org.glassfish.jersey.containers:jersey-container-jetty-http:2.15' + compile('org.springframework.boot:spring-boot-starter-jersey:1.2.5.RELEASE') { + exclude module: "spring-boot-starter-tomcat" + } + compile 'org.springframework.boot:spring-boot-starter-jetty:1.2.5.RELEASE' + + testRuntime "org.spockframework:spock-spring:$spockVersion" + + compile 'org.glassfish.jersey.connectors:jersey-apache-connector:2.15' + testCompile 'org.springframework:spring-test:4.1.7.RELEASE' + } + + task cleanup(type: Delete) { + delete 'src/test/resources/mappings', 'src/test/resources/stubs' + } + + clean.dependsOn('cleanup') + +} + +configure(project(':fraudDetectionService')) { + test.dependsOn('generateWireMockClientStubs') +} + +configure(project(':loanApplicationService')) { + + task copyCollaboratorStubs(type: Copy) { + File fraudBuildDir = project(':fraudDetectionService').buildDir + from(new File(fraudBuildDir, "/production/${project(':fraudDetectionService').name}-stubs/")) + into "src/test/resources/" + } + + generateAccurest.dependsOn('copyCollaboratorStubs') +} + diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsFraud.groovy b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsFraud.groovy new file mode 100644 index 0000000000..a47dff32e4 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsFraud.groovy @@ -0,0 +1,27 @@ +io.codearte.accurest.dsl.GroovyDsl.make { + request { + method """PUT""" + url """/fraudcheck""" + body(""" + { + "clientPesel":"${value(client(regex('[0-9]{10}')), server('1234567890'))}", + "loanAmount":99999} + """ + ) + headers { + header("""Content-Type""", """application/vnd.fraud.v1+json""") + } + + } + response { + status 200 + body( """{ + "fraudCheckStatus": "${value(client('FRAUD'), server(regex('[A-Z]{5}')))}", + "rejectionReason": "Amount too high" +}""") + headers { + header('Content-Type': 'application/vnd.fraud.v1+json') + } + } + +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.groovy b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.groovy new file mode 100644 index 0000000000..fa8cd88ade --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.groovy @@ -0,0 +1,28 @@ +io.codearte.accurest.dsl.GroovyDsl.make { + request { + method 'PUT' + url '/fraudcheck' + body(""" + { + "clientPesel":"${value(client(regex('[0-9]{10}')), server('1234567890'))}", + "loanAmount":123.123 + } + """ + ) + headers { + header('Content-Type', 'application/vnd.fraud.v1+json') + } + + } + response { + status 200 + body( + fraudCheckStatus: "OK", + rejectionReason: $(client(null), server(execute('assertThatRejectionReasonIsNull($it)'))) + ) + headers { + header('Content-Type': 'application/vnd.fraud.v1+json') + } + } + +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/Application.java b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/Application.java new file mode 100644 index 0000000000..2089bda142 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/Application.java @@ -0,0 +1,25 @@ +package com.blogspot.toomuchcoding.frauddetection; + +import org.glassfish.jersey.client.HttpUrlConnectorProvider; +import org.glassfish.jersey.server.ResourceConfig; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +@Configuration +@EnableAutoConfiguration +@ComponentScan +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + + @Bean + ResourceConfig resourceConfig() { + return ResourceConfig.forApplication(new FraudRestApplication()); + } + +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/FraudDetectionController.java b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/FraudDetectionController.java new file mode 100644 index 0000000000..bb015f08a4 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/FraudDetectionController.java @@ -0,0 +1,38 @@ +package com.blogspot.toomuchcoding.frauddetection; + +import com.blogspot.toomuchcoding.frauddetection.model.FraudCheck; +import com.blogspot.toomuchcoding.frauddetection.model.FraudCheckResult; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestBody; + +import javax.ws.rs.*; +import java.math.BigDecimal; + +import static com.blogspot.toomuchcoding.frauddetection.model.FraudCheckStatus.FRAUD; +import static com.blogspot.toomuchcoding.frauddetection.model.FraudCheckStatus.OK; + +@Controller +@Path("/") +public class FraudDetectionController { + + private static final String FRAUD_SERVICE_JSON_VERSION_1 = "application/vnd.fraud.v1+json"; + private static final String NO_REASON = null; + private static final String AMOUNT_TOO_HIGH = "Amount too high"; + private static final BigDecimal MAX_AMOUNT = new BigDecimal("5000"); + + @PUT + @Path("/fraudcheck") + @Produces(FRAUD_SERVICE_JSON_VERSION_1) + @Consumes(FRAUD_SERVICE_JSON_VERSION_1) + public FraudCheckResult fraudCheck(@RequestBody(required = false) FraudCheck fraudCheck) { + if (amountGreaterThanThreshold(fraudCheck)) { + return new FraudCheckResult(FRAUD, AMOUNT_TOO_HIGH); + } + return new FraudCheckResult(OK, NO_REASON); + } + + private boolean amountGreaterThanThreshold(FraudCheck fraudCheck) { + return MAX_AMOUNT.compareTo(fraudCheck.getLoanAmount()) < 0; + } + +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/FraudRestApplication.java b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/FraudRestApplication.java new file mode 100644 index 0000000000..ce70aa1050 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/FraudRestApplication.java @@ -0,0 +1,13 @@ +package com.blogspot.toomuchcoding.frauddetection; + +import java.util.Collections; +import java.util.Set; + +public class FraudRestApplication extends javax.ws.rs.core.Application { + + @Override + public Set> getClasses() { + return Collections.>singleton(FraudDetectionController.class); + } + +} \ No newline at end of file diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheck.java b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheck.java new file mode 100644 index 0000000000..77471aee19 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheck.java @@ -0,0 +1,29 @@ +package com.blogspot.toomuchcoding.frauddetection.model; + +import java.math.BigDecimal; + +public class FraudCheck { + + private String clientPesel; + + private BigDecimal loanAmount; + + public FraudCheck() { + } + + public String getClientPesel() { + return clientPesel; + } + + public void setClientPesel(String clientPesel) { + this.clientPesel = clientPesel; + } + + public BigDecimal getLoanAmount() { + return loanAmount; + } + + public void setLoanAmount(BigDecimal loanAmount) { + this.loanAmount = loanAmount; + } +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckResult.java b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckResult.java new file mode 100644 index 0000000000..28efc573f5 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckResult.java @@ -0,0 +1,32 @@ +package com.blogspot.toomuchcoding.frauddetection.model; + +public class FraudCheckResult { + + private FraudCheckStatus fraudCheckStatus; + + private String rejectionReason; + + public FraudCheckResult() { + } + + public FraudCheckResult(FraudCheckStatus fraudCheckStatus, String rejectionReason) { + this.fraudCheckStatus = fraudCheckStatus; + this.rejectionReason = rejectionReason; + } + + public FraudCheckStatus getFraudCheckStatus() { + return fraudCheckStatus; + } + + public void setFraudCheckStatus(FraudCheckStatus fraudCheckStatus) { + this.fraudCheckStatus = fraudCheckStatus; + } + + public String getRejectionReason() { + return rejectionReason; + } + + public void setRejectionReason(String rejectionReason) { + this.rejectionReason = rejectionReason; + } +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckStatus.java b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckStatus.java new file mode 100644 index 0000000000..b87c365d51 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckStatus.java @@ -0,0 +1,5 @@ +package com.blogspot.toomuchcoding.frauddetection.model; + +public enum FraudCheckStatus { + OK, FRAUD +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/src/main/resources/application.yml b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/src/main/resources/application.yml new file mode 100644 index 0000000000..a30a91f034 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/src/main/resources/application.yml @@ -0,0 +1 @@ +server.port=8085 \ No newline at end of file diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/src/test/groovy/com/blogspot/toomuchcoding/MvcSpec.groovy b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/src/test/groovy/com/blogspot/toomuchcoding/MvcSpec.groovy new file mode 100644 index 0000000000..621d20ded4 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/src/test/groovy/com/blogspot/toomuchcoding/MvcSpec.groovy @@ -0,0 +1,57 @@ +package com.blogspot.toomuchcoding +import com.blogspot.toomuchcoding.frauddetection.Application +import com.blogspot.toomuchcoding.frauddetection.FraudRestApplication +import org.eclipse.jetty.server.Server +import org.glassfish.jersey.apache.connector.ApacheConnectorProvider +import org.glassfish.jersey.client.ClientConfig +import org.glassfish.jersey.jetty.JettyHttpContainerFactory +import org.glassfish.jersey.server.ResourceConfig +import org.springframework.context.annotation.AnnotationConfigApplicationContext +import spock.lang.Shared +import spock.lang.Specification + +import javax.ws.rs.client.Client +import javax.ws.rs.client.ClientBuilder +import javax.ws.rs.client.WebTarget +import javax.ws.rs.core.UriBuilder + +import static org.springframework.util.SocketUtils.findAvailableTcpPort + +abstract class MvcSpec extends Specification { + + @Shared + WebTarget webTarget + + @Shared + private Server server + + @Shared + private Client client + + def setupSpec() { + + URI baseUri = UriBuilder.fromUri("http://localhost").port(findAvailableTcpPort(8000)).build() + + + ResourceConfig resourceConfig = ResourceConfig.forApplication(new FraudRestApplication()) + resourceConfig.property("contextConfig", new AnnotationConfigApplicationContext(Application)) + server = JettyHttpContainerFactory.createServer(baseUri, resourceConfig, true) + + ClientConfig clientConfig = new ClientConfig() + clientConfig.connectorProvider(new ApacheConnectorProvider()) + client = ClientBuilder.newClient(clientConfig) + + webTarget = client.target(baseUri) + + server.start() + } + + def cleanupSpec() { + client?.close() + server?.stop() + } + + void assertThatRejectionReasonIsNull(def rejectionReason) { + assert !rejectionReason + } +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradle/wrapper/gradle-wrapper.jar b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..667288ad6c2b3b87c990ece1267e56f0bcbf3622 GIT binary patch literal 50514 zcmagFbChSz(k5EAZQHhOS9NvSwr&2(Rb94i+qSxF+w8*h%sKPjdA~XL-o1A2m48I8 z#Ey)JC!a_qSx_(-ARs6xAQ?F>QJ}vM$p8HOeW3pqd2uyidT9j-Mo=K7e+XW0&Y<)E z6;S(I(Ed+Bd0_=<32{|526>4G`Kd`cS$c+fcv*UynW@=E6{aQD-J|;{`Z4Kg`Dt2d zI$)UdFq4$SA}#7RO!AV$BBL=9%jVsq{Ueb7*4^J8{%c%df9v*6=Kt4_{!ba$f6JIV z8JgIb{(p+1{!`T5$U)an0fVi9CwR`^$R`EMcp&rQVa-R*4b4Nb_H8H{ZVot=H7 z#(J{{DW4ze_Ck|1(EbPiGfXTO}v^zl-H!Y3ls9=HV&q>SAGP=VEDW z=wk2muSF2y_lb}fJxZ}al~$+3RF^U!k9x5x zWyl(8dbQ0`AG$%Y?*M0m+cp^Qa}1udZW_Tm3>qdzZv!1x+<_Uf(p@M@ymKp>OX9|F z#L1je z9d6SUXxx2fS*7N*e<;=+3&t4*d+M`}GIPJUbTo-OSVjvF3WrfXg7*_H3ct9cxJKZ9 zLrMzth3?nx0{#c^OdHM`vr>x#A)-roI0OOn<=2h_wo|XV0&wMtLI5!@**l*_XQ2R` zrLSV49cUPRsX#(O5oQzZaIYwwq8Zs2DLXGdDKbr!Yg?7fxU|>+HHQ`48#X--yYCk5 z2_CBTW9rX2eLQC0%EyQli<87+%+Sy))FFW+RMC{*hfJ$|;#$?pAT~P0nL-F}%M*RxwBh)JT4trq7rR7dHloLmiM^IC{>usB=4fXXH9NMyWznFd(bffDK zE@*_maXO?|$?M^W>jXtsnk2}7g8b8%oLp);SNzqtjlYHDKkJ?J|K42x(kk(o{=Zub zF6?{i>=+HX3r6qB=&q|022@z-QLmMSLx%Up}FGL44Gk+C_QL5BU+!i2(vEvNf8Z)-btUdpVY9ovODm+#V7jjU7Y!AWEnY5L4 zy;^;=x#{x<{pUJOVPj)cXJ>gsJ418R ze{ZN{4Os^?bu@m)^eIMs5MU5c;IIG|=#WSfkfeyP1R(>Iv2Y(9if76Ptu~dWzdSmPFUp;6Ezs&WmP-Mn-9ah*g8e8 znAxyrWhx~~tuF4fFyFI)v-S3=C$HmPHmqv%hb3*;ljbj9zaA_}QvfU@RJCGH%&3Mc=GR}sQDh$UWT-8|{1QwhXWO-dM z3?^C@cbP^-hfFljgacs|7mE%a1FSMK5?o1{VuaVB3iP=LvFEL@C0pfwirZ4SXxMUy zrMG05M!9CU@G7-}bgjI%x$|_B9Z@Hc86jXlPhZpJfk@$BToMpqU8Y zS7rRkdp>e0{86ZjFbE^zkdwV*R|JV3EhCJcqjJlZ1HJnbe0I+>a5?HpHLs6A`4&VE zZkHUK@cLRF?y^Gi~ zzERBcPdAs0R^=N{aeUhK(Oc+@?mb~Y)__*Dt{8Wawz6H_)v6niTA_*_%)UP`0`WBL zFONOa&+T9+RMF!QsgKq(%Ib;a-!w+*&V)Y#Xz0(87=H{^VBk3UVeed$SFCL{IJMl-`1FQ@Es zq)F=J+jn(WH_*lNW;=>)d5ZFyL~O+t;)Rex`&~h0ZJ`wg7K@*lu0E7;tx>KLWPduY zB{4G}TQLJE$Fp^?*3raESC`NSpmv`$M^ zR?`+VFj;fQu`)I4O1dHwa_R-0y`qHjG*yT1*ta##G_W-;1ira)uP6}+r|OX64}vD7 zCfB#p>H^?YEyF6K(H( zcSh4u5_|{iq)=K{S8Z{@n?&h}u!l2^EP#?v?Obp5kDl`o9~up%2*s>1Ix5~kT~M3` zo9Mg;n$TcwaN!PHHbuUUw3tRqYfjpz$rm9)1|S{rtPnG|3qao}1W27Wig_4j-(rTjVi`D@Hu z`P>h7i$K>zzc1rQ!~L?29sG(`4ewg^)@Jc)II0KI)@q=D4CEaX%j&RlZ>Dhv0p=|f zDJPQ~ioTP^ju2_j2(V9haP$r!cTNIK`eUF|-}43c=4*G09&bROE80IECDekrK%+jW zBayIlJSDqrri?dj#ZGRQI45{XfBLkOiWIkGb#Tk>GU0NMA&{q`1jQe9jlfJZSTNF_ z5nD5A=Z=a%6uCagCu3np^0R1ibyV8p>-XWfFJK2Gb#o`L=pCm3Bz0F-w`5gv7zJaA z)RS8mWR&`<;DgOxA@S6FQ*5HVF=Pi6>}viGQ3jbA1*0gz7vev?ig9gVhr!>t4e76E zq5scb<;TCmT2XsDGfQ(RVj)A|h<&2OW-AJrbhweQvr{uOf)AdTJN|xO zAOSplNX(IEhc4?4!HsA&Vy7Ayn|y;{2-yn=}+S<{JboP z+O;`IR0`XIjUt&s+%;#~ImRt_GtRFatr{*eLSOp`M&L2~I&K?Jn-<|hTDADdW0!CI zT`L(i=DpZ{m#h7}m5b)AA2rK@4IrsGNhTCLuA(5#C4^ihsG8k9wtfgz{e1{i2dg)4 z+mI{R5E#Qkbkp^PpXHo%=j>nj&GC#hXN&B=ng^Nz`nHCfc3$|&N@`tY-`ccR_&0zX zWOMW?UqQVp6a|9)%p$rhzNSyZx#rwXmnhl-bz2n%^a-VY_->1Rq3M@UM*B73Rbh3KcNU|sUv}tj}yqehs%OmelPMB0M zliOnQ$*!7!%0vXViN+eRgc?|(1-`Kgq(g{Uq<|t%Bz*Q}Y@)~Dxqfxxh@oH`C}F!u zVKM>}SoSAuA}tUnZK%W}VFDOojbWmn1c%601hYWY6h!VJL@bC6^kD6@5DA{~rDbc` zz$!9AztbeXVgISB%D(uPM}Of3_Fv4&^q*DrzatANL%Y8i?%&Z*jK+mCsyf=YZKlbf z+hn1Vj7%sLh~;}k0J;qf&74dzBAF6hP=~yIQm6^14M!6?dhV;l=Kx&n;12=r;6bdu znKAcoswa2O{OPE5Gq3CJ6W7_dZ0Fg_o$rq~%z)3=pMwn1WgeoUs1j^hLuCL?_E++U zUl8cV_e>1#s5BJnSsHgKVH(k3juJJ{(latn3c<1EL^IYNxQh#yBCy;2!x%aPorztP zjJ%Y^H`Yu{q|z#bbRlXv*1|BB=p}$j7!c7C(+){=Hpz}swAa{;Mv?w7=0z0L(939t z85~w@r}dG`qJ(r7Jk^{@x!g>S2N}H{+N(b&vsMA1Z#qSh8<*eRxUKlI&Oa;*Luox`bScaqq#hN!IK3bgB zB`i9szi)5mm7=-Sfccdew3}(DLGfBO@@O!zHa3jAA@asvg`6x7z?j<@r!?HkxDGl; zA4MQQdP?iygX<&#Pt&fZ>4)tZ`4;uBW9N{x=T%*k!S#nf$>KRy}>6yQy?^(R#_fv9|9gTaH7IwKpOb=Xo?gi;akww64+&sf$z|_oI zuZahhq^LF60F>Rc%fkD!7@rigV#kVa^+@?Px~$YsNR3)QPBOZ(f96@IYTBerb(63c zz>}2iX36tDclpTaec;b}1pAap^JYHW{v(X;O)ygVC?+2IJ<4~lV|hQY9F&fz1UDoX5607wu*7FLP=u_rpZVqb zT#DD($Gu8`ZL1j?)6BP@h^#Ro?+wo>lacs#^O^h3c%lrP#Tk&f76F66$)uko$~U{i zFxE>!FOr^ZN46l7O(fh3ODY*ED*fGB+br75!b zD9RQm9(DT(;y?RI{yGj7%_y8*a2V>LYb1M$e5qJezC!U zR-eGYfjYJ!gD34F6x`2&w_<7T-E^D#yUo<&OS zc1dmXr~k)`Uat3yd(Xob>E|E8mmLrXobN;jv|@g)D0OHYJ1I8rlyDYAbYvcT+%8Sj zyDTth@@-~MGjYR*#RQ^#3j3XXL*1dUkl@#l5XF0c^E)53T$DRY=-htu!q=>j*#p?F zSCUz~s8xl*&iOy(^Ngfv-XmA*;GBW zd)}`C2W_ashy}02xm~3DH36VWBLJ10Il7Id6nt$~7hora6?Ils4LaFoFuZm?UJmAT z-3&$(^VAx-lSbLl_O;C=Q{eh>+zEMdU5!VT4k3ic1#w_+)-by@fE^>1sU&)xy_ws4 zq>WjPpOyZ&8o<pKeHD!`!)ch6}P=2?*1GiR*lYgDdHl?x-o7`hcV{KiLo}+xZ%sf#cl0pH_6K{bq zJ^!4l)|nnxEEZo|+C^#VtxL;YGSGqvxx;)O*@`@qRekwLLNq6DAOt*bI;>KPM!}** z*1Fv^$Ob1f_^3hhEllh0rml_3l0gYu~zep zi*ck$)DHOCTC>mzKw9~QfB`qEqwJY9v`tosEI@3GmTICiWK7~mMjAyp`O1}(QXfHS z>I0_glIrf2a);VQV~kDfQmL&R&8yX3mcimT!67&}8=24)t$%BU*8A&@Hs=$k7KZC# zTYN^qk95D4#q5?W`MM}sK)U$CCNE8|C%e3CXNafxch(eEGL_+Piz|4%*V5)8zAF*P8JmMUCYz%v(Y>ssFWfrj)^We?D7Hx)U#H`)OGH2IiptVS z2*zF^F)h%($!r@~7>1<19H#-i?~NUfQGG)@kw(C!+efD4E|L8jmIO9uP6su+9Vme) z_Ut*1ruchGUdny9ogKS9J#EHo68*jLp!D!uee*%?fo0~NSf8QchIDo8oULzpP`tQ3 zT}c@f(sqT>I-GJSSpkR;CSJA;>Vy5h`}yCCQ(YrT&O4d3zYfl}u(z6VCE6!F;F*76 z9j0J8{ssW#uLmNn53($aP9>wroVI83#TbxmSWb`TR@1fFW3)dyT%j-X7{NjG)mBPt z8z+G-hb{;ve{Nq7hNHIcwvmwURm%F#C{Jia_1Xs2a;#VmHY@`q_oFT2!7gKT1L$_S ze4X%%XFJ_o4wSPX)sr=BrRLuUVxO2k%NiH>WW1LwEI*K{3Gz#YW*r(J_Sjb*2iasE z!QPPy6q}ec#&eKI67nf|({Azk6jE$x>w`_s;hWgIE=e_ovbyj_2_8Fh5WIi)Q06ex zK_rmt=gfYqkR{}_CY95yTSFZsiL!^3CJvV4kYI{vBVoSPTEKg^5Yhjh6Q*qkbl3Z` zxrAGk8TrF!V-9SzKxWt&%eP$HlsQs0ga${AUpu%Lh1E=Z@$g5?rRAwX)DueM5vQtCS;kk&S~>Q(zA}iXj?uYPSN2g;`3 zr)tMR>iS6fS{Bt4(+lHMq?p7GTTP4Z-3CxC>~=?1uq|2lu9RZ)h-_brR*o4NcMfZt z>9{-CUh@iJ&~YV=FmZ$@bUu>LCHA9Bs#;S-ykkxyG&;)aSds(|=LmlnnN>@$5#y6f z52PWa7ov;Cg&4n9^e8SUIxgmgdaGopW=?jeS>5hOHimVi!ixB z&L3V_Y{(6VZK+dE@^d&Lp5biwj+@@G6Y|R6E7bpetG}Z6lodOa3o-q%rZKdO?53uHjV=~>M>LX0e}LqA0#;Wi z>Fi99*d>>vgM$sFrG?jSll(bPvE3F0SBr`E-F%7bVw3zL1%G0T0xl)LpRL!9rRcZ4 znW820$m!^d?*snLNAF9IeeeBXsy=xE{l^`V_?cqSTM64v;<2La{6~897oU{tV~NPl zGm`(o6A}0+qsbLx@tZ>YcEJtAnfK!lVXycvt&CpfQ~O{wVSh^PZ@v7R)Oo=a~+pMUfd_P;?MMbq0W zn5d_K8KCPRQ7_>a%$}tW5E}*pRTz%)226#|i#S263Qo`)>UAV&gS!BZJCB^* zD)9KKv*&q?w2V58r&^+i9tld&yUj=}t)c(aVaT2V_ry>mvCmQ%m0*}^30i0^;xDFP z#GK)q)7zR!wDLf_FI+hJNHi+CQYLx%kd$c4;YQ(OP45JYT0gFhYtmR|&A;F>cY8aj zC{lzsg>cZL@c@)hdyj$RA8y!D!n)(iTko!hyL)Wp!_&LE&D6}bxGl&Y_tbnuS`jQY z(f*_-X`iYEoxr&a*76lkZCe-a5AIOXCY># zbiVD(DT$0EI=U*Yf6Sl8f6>23pKEMNQ4Ajg^{ZHghmvEQH$3o{ms4*o6hgYvpNE+( z#AZ;x7E{DM`7Hvh|Bml=1j#gyl{K&_{-jEI@)yyKG&XZ8%52}!B`ZE?EL7#WtMBKol?Mvj2saaE<61>mL%<6)IXN}3^`@*!@} z341EQrH}dRV~Fjv>F3@mjwCOV$Y%oyGr0LwkxkuPb6X#ms0o?9o+d9{x3cbiGKmX3 z^!+;D#Al?M&g?P9kq(7|b*i(XsOwP?H!ElS*uhTDBDKArqGP#E7dcE;HWkvkaEAW? zF!3|NMZb>RCGHa5#)`X}8w)%}Ey|gW@8DUXNsDR*{esPO{W?k2a}RxGK|616o0)}e zw?Os9aROYmtw`mSga!UI{x(DS%Vyo@y>JF`^Fi2A{GhSfM8=YCUiq2tRfBwSZeFh1 z8SG=1Ot08%#iR0jnhZp?#@V2YFnQ7qP$zE3&#`>FhsO>}OG$enmf?*FVG@qB!C+bO{M}K?d?H2@pq=}!TIg&Q z<|^+Ey(ErEeOf1wvGI?LX+DEA>A4Ka7Q!%PAW&4a-t8+>1M9b(T0qACQ=f;57D`tu0g(=;a7O*h_Jc4JEypx1gs; zCDX69d|g$NsXEuD1H|$3$ZHE}u3HP4b!9=Q%rqHBgCfvK3>j?XLQkgDUg`93gF?}s zS4$rqaDE(s2IL!2Y@kw=(NL~wa24NU3sm0I71mIjZ>?9}bNl5^Al?Sk^y(`qsW$ER z@g$;Pyb*^A=G{Yrb0a>4vvBBZ5U2|)}iX;AAo6X<=K0YOtm49s4edp~uvJxx$&=o-&rGttC2~o83 zfuN5-wJBS(4plr-Qmhz$`*di+<4KB`>;9BgrbANhj6VsJNxLq5IoU%8vF$2M+Z2ek zTw84Kxg}m}jc^*zK>s;O8dE$R&kkO5>*Y75eKaR2>i5fb7o!D~D0P;E`CzLz<48 zBzH@erfNN`nS4Uy3@n#r)*^n}uKHeJxygl)GV-F`w49%s`cYMPYi5Gahg$5e??^in2I<7 zUKZDwHf#riMrllW@f~Nsm&l0q?KJzSfp9hXd2pb;UnzJj^xc9bqY2zVLk%GU)}?}} zB7(TNFqdZnN}qRsHgj1;xcwQt^<58f3wN(P=y%mH3&}An)2M$}(>TF|q1;N5^ZX`t zd&q8vtB(q@FPC>=6)%sC=t3jOE{U+j(IShmITq`TXA`_QKhoBZ7GXEN9MCEV z+~@7gbqUElkbsjU7o$HOfy49&nNHI)#@Dt#fvePViP1MzItEa|goh@hCZ273Hd#4Xdhb+D?L0E87T>DawyVvc3J#zePjBG zaZj%zUc`L}>#2=d=9E*RS9(6nm|%{&E`OI4~x8fs!0ZZ3b-$x(I3NCjCbUBu$h&4 zvkoaim?yiSh1?-2osDeuCf;fbpe3>H#44}rDb%z#W=Jf-*l&-c4uk{yAX)0;9gvX= z#)Ov%5_L%}8e9yEMI=PVh2w~CbgO6&n#>WB?TO?1h+5Yitr3i}=1JW98CC66#>33g zXG+Th=cRh7?7HQYiRy+vd{ov@)w1~xg@TuyK2?xGWXu88_2%M2@eaFd&c-wqqNP26!WU&USZ z8lIHzv`SrJIVF=z2amJL`aB8>O7!d0X?{4zEM+hWKZDaY!_ekJhvtHd^7?hm>;4d@ zeK2Evnj=*zE(YguNX`-&354G{M`WHLvobFJIa9yg@YweQb2NV_p4&_KA0#<1V4d`|3w~@!Wda7`st< zYW?_t6&a=_{Uf&^ zGZWvYxn={#fj-{6v~}bU*&E+%&Wlu@!G)AUL<|!YF&;Wt5x}BM0*{RdB?B3}`gI!y zj553FXs}D9SFRVNei9isSJcMC!3@^b=ePm!`OM}?eK*P2HgZK{1j$CJKRVD)>81IkA@&{z~;ow^HGAt9aw-uE=tusp@Din2k-hBfMQG|V1erRt^^#(kf zQgupM_mjXiJP~C9gG88#+vMpN>pP3tsvec=R=AjpK6(QH<hWIpOCT{1tvWALW6Lfn1W{#(itOApM^OhR99D@A%6#OSz-s+Q!9QsS& zCI3wh{eNMaMeOZeoL&CX&GLqpcB(FhPA>lsclT3!Lj#F_paHxBrO$>L%mD-~b67!D z1~-olI4)rvJ!SWC8`+8~*2V+>RkNnOb#`h)vdAAyqV9xtx zMECS`Ugw#qZsX6lS$js{u0TT5SH~X`jAmqAjD{K#w8ti!gI&?!boYkRVUWz&lbU;j zpI&^siQ!M0$w;Y8f6pGRQGT1+7^n_FK1n%n#=X`JhmStJDve0KY7S67DZM#qOJF9V zsDSvWX5_Ceg7D?vh5F&(%8r5@;-NmtUM&Z~CdhPHI<~GF>GNyiKPMbBbs?{JaFpUQsE*gVRbs zEv49jG95i*$&=}FTc(jg(zL{cLDWfnG7V?guH&aE6kMsRMlX`f2A_$)&f1YNJtD_G zEQRHuh&2^kQ#&G~_Tdnw#7hD^OP={T-S`-#7hL-v%-Yo+CsrqZStHFQd`|C z8@mVz18m8%DgMB0My7%LL@iHak7P4Ah^U6z1F{v&MJJvISf*T}A7KH-4c%fj=~gT- zHX0-tQ8*3d8Qlj)Rv5#D((4pQe6vFQ5#(Tu-+Z>7YHTlH?qLbF8gNPN0T2b2KiU7Y z;jIP@EeRtqcp`2R$~G6e(rg>M4-2lpPYXVK%YNrH7>6+!ClN+~>M1+G3DYy|u6FqV zRLMQ~o8_{~0L09EDk}#Drv!hg{E`E9y_4=#1q#C#0`sN{g1wMR!Sa`_E$=8l7$jsv zFf#b;9e?<9a6td}ThnPw7AURoVe|BpI*p4em5$dICdDLevr=8O`p=QEhH8?PVXfAZ zbbP^ybvo6rIsgHUB3EtV8lqYhw%UzDJtP{bt^XjXYH_o^OqFd@!kwVvTk2 zBG|Ahenv*#WTt1SAkrj_V~5HSuQ~GpT{->!jrjE-v`Zf|a?upEKsR@Z&l7eVgyDKx zIDZ1gJEvHlP8FUycaZm|AJ9DkFDoYnx0Aj8*#)$Fy;@{GLD$BQAC4M>u!Elq_c1vzSH@#&FR16q3Cxx4oLvwP=f+<@S8~wy}z=stlxT|jUJ$d z7cJ6nZF=Hr*d-9e8FDv5WjBhiytFq%g|TaZWe+eyM);j@Kh4r59@aW>%dyuZ8`c?m zc!k_0dh9+i(|LHI1a_11#?R8l8T2y#;fF1N)DLO;6%Q9a*hU$I7&Q|Ib5;cq%!c5DCI5wVr|1{4;5WVk%7rjfIP8hpujO@b~BuVlr29_JWtJ>hp z7A;x0N@bFp^2W-7ryDSO`!nIbok@UDoUw;UrUz>{_12X6idNYNT|a97;#C4{N3E`_ zl#!ihVWru$$=`n=h^UoGhbts>^OIOi!t9sJYex zcWq{GLBO_(QPq~CfvsV?m~BeoXB4J48?9t`7{IN^B2|pL#%|)|Nk;&(8 zd*p6;RXJJ*U8;8rG}ClE(=G}neQYM7w-S%n4>B$Z>5;c zaaFy_anPH*Iff?(4tOo)x{j(uWciGp(pj(CdQ#uE`^6Y1ad1*oFh&s7K9B@aLIusr zvrQ%{S7R&HqK%>e)vG1@Ygnp=g=GVM4CsRWisf_%v<(c^d6lo&1V8SaHp});3TlFk zG#e?^KSZefsKd{jB?QFNTvMNZINe?VKvNGmoo=CYRU?nvmJz#3kon4YoO}Y15~ii< zw`0%`p{>o+EQ~{}#TW!D&T8Tn7_+A-&mOYP^~>Hl#q^H!spWjs+8YbVgxO25UOsUN z(<7r#ZN-Y|o#k}~8SSyJ4jSgG2g5<;8IK%#EcoU}Wcs;K2RA|6f@+&2uZ&Na1+{y! zT;JvU`mgR--^zFT-XJi$H?~ClDYfY6LhB!_Ny7nx=U(#ANjOQ9v`?>wfw~{iF z7iy``+ne+ZHHI(z9M$i67}3t^eaKrOdU~_qpt*>I&Z?*lwH-fFVF%MF>aY0Bvhf7h zAlI1y-Ljs7H*OPTr(#w$4n^uB3aSI_pVg&-Ocy-|^KzFz4#@0e(^$H9Rh3J`ozlWFj&MQyrIxnfkda8;6m}LjSsPrxErj|osSsJ z&jo8TaWE!yAfCfv2+(<<2A-cY#~I^>HZ4vgd5Ba%XU?;u7MVy?F>|NMPNIp0#2YwiZTB<_ip#a=5n+UbTCvk^-;PCb06bq2hu{kC=ala6;aYD60)q3&7JGDnwT;z^yce=7daJ|-puuzal;!BAu=ok#ta0d{S zOY92%j^NNEC64@f_q2YOc@2K3Ht#+bkWS6Y!U$76?$E(tBS1TRu+X`T2%Hm}5 z$G}vhy#EjY|0ga-lGVSw`WuMSVgUis{O3Sa@_${0{C7C|Ke73L@!2|fe?!sUI;Ke` zG81Cx%rq0!BnNN}RAaayDqtfhT%j2wn*)>dzVn9Q#zt;0E5$3rjmJ8z%IBu%=ye9Q ziu%-+=bG-DKXos@+J71BpU-J{y9vdy@$Ie-Mo?j#JCH#6j@d_NnDSN{J$ImxhG4K1-AAJTfTm@y zkwzeV_Rk%-=W&#uk92?P(Z!F$V^pVyN|aM*!5)ghp6gLgG#}M-ClQ35`vbGLj~2om z#_4u1a$ZU2izx8ddYMF z#gRInDsFTHdYY+T9h!q^ZnfOxy2;G4E6k)B4e~PG{ge2GZ)yuUx}yanU0KWTuO9hM zAl4TT(^-N^1gg3mHB{CxW4JKcO>Cs{3~?3jBrL*J%hyH&b%TSTTfw8KEq-*gOqL&x zBZWS*BO+mH>r{hgLv@J=;?sO_9}yLN`zURJ3d(e(mL*kX^EqTO`HIkVlM}zQ(-hXO zS>mk1Rq9_~1CZH`7Tr>&0%wz*WIxwF^^1D^DWSKD)FAOaHzV})eyPyk7lnyNSfvfX zvTsJ$<&E_C92MF>*AHiq@?)`Dn%#|_Qh za(?Iz3f?M$e=pqHe@G7c-=TfhAzl1=vV{LG^mW8*weTRwsf8xSpe_(Y6;P(BTOe)e zH0_Qa1=sMjam$cIbyOuZ*XZDtWbcCGoVO~V+mU;qe0TM3;7?~O(LA7&E*(98L|$`0)graBHY!{tsoLS4* zluf$Jxt+S9_sS4?5D}yUJ0nggbdNR+!=$b!h6pOJ7%i+~+c5ZwSf+{kbP-D&0%eUX zQ~3L__Ams-qVs8?shPyVRnFEI9Fp(D@&g=u6(gt~b2;Tkb>z~ogt}P@EsP&6uY>iG zzr;6e=_=-iC&naxoa>OxsN>Eu*q=F0tZ$tHiNTJTSD&^~LgBrI>2_Q$j5HW}XAx^ym9D&~X_ zZ_d}T$`AcZkQ>;eg#ldX6`u3%Hka9#NRHaAu9V$8sxVSSb>3ZcO(KQ%An>4%cDST>@~&74Zl{1mEkXEVt7jfO7|_C#=ks<~N1E3-dd z9qn~MPSEoiE>UWqUA(KL#Q-MurE7nxH_S+FA25TbvWkZ}*8HNVj^tZ7R=h-$QaqQY zlM;O5?N+dZ=cPqE@}}AZibpMLO`nEc^Y&;^n3PLhyv)PH-4Q&p?wn>>;u;mqxC{*y zJFao4I#f74vU#W3H%_)UtuFXq$XfxSC|+6m1*M}im_6&DaXAqq@;?u8XYrceVyP}w z#Fx`%3{x|1G;_=f72Ui5ejJxJiW@F=zijT=G>-*gO?}u=Bwq`7*){XtvMy8Gg~t@p zH(XNd5Dc4usl=7Gd0#PxSl`e*Fm^EWvmS!Eo!@E;teuWOvo5$FfOC-UC&Lc7ehm1) zMDB2bI_8QRInX&{{5W8eoF?xBqj;l}gj-1jgb%adCJ{Tl&|!#Ym?<~p2G_bH6dSWr zSwDgMT2d7X>z|3<#wCMIK#uwVyE4T9*qY|K)e@~7tu5=+7U-ehaTd$0=re~GG|0;w zn(1QtNXrxoDaMvftH1JkJuxOZcjC|+%WUZpQ#facxj4d;jRVyix$Ge-PwLF*PILR$ zu|tmQV&Q(5I(`M|bt(@#lKQNQ$)DF@!D|LeSgnUd%|*-32PxWHB={GuvWjv}CbLOcpbin3wj(wkwzN9OC2jsj2K+KWXr)1Uw7lIYfp4DU}iJ|6y zWsYq>Dkcq~r+WFGO&6ZR&t0p$tqB&~%nUc3ou{)6oh1SGfeR-8W88{uGPelX-T-ke zk`7;RfYs4s#!&gfZyvhXNf;LkP)iG5@iIpnJ?xcdtgxUc&Kg_BR}ZJ3XWAinV$R) zekh20+d^x|)cdR~bO$Lwyi`YnOZOBa7# zzs9L-LwYI;h*DF2&7Ld$QSF)^U*^T6`wCZjm~2fVFHI~TnVO4YWA>)R+=Zm2?6A6%&V+igaN5`0 zQ?mRv#Ul$=hdpMH$oOb7jIGKWM3f~!?3-=X_7kpT{GtHDzk=oqAJ10Y z&yvY$`2V}@z(1s)|Ly4Usd3Uk(?I{=XCY>ej-=AApsK77rRr~}45R|pwibhcXlQhk z$~JOMk4Su2yTlDUL7g9Cm09`lbuZ!6l02mU)YRuXA%yi{Db|l zi;hHuv<0(K38!#VRt(yIZAFxQy{$!*jYdT@7p>hFX+1#9U#rJi*qt@RY^Ml!s-Aur z18QcAHkMGt^2-^xM@bI?m2rOM z;Wg#_KPzwfXjRk8K)~k^XOrE_^T?AD$z&}IRog;`Xu%~W(x6UZO)woHQPEKD`?(y+ zy(_yx7vn)Kf_d?+Y+}vop1+$Dr8U|JtR}Oh+SY~2_01TA?jSR}REUWo$^F_~ zugzs8%a_p4A^^_N8pbR~8jFsDb*Po)3YQw!)kk7w7QNWJ(A`eaVbebZcD zD$|h|OFU5+OI_a=$}~f~F%Z^*Yqfzq?Q+m6oQh7knGl%rzs~@@?7OSVttd&2ks4QJ zk&9P6W~DtmRXgyLi`xho4m%Z*P0e1JW|v!fUmQe5>Ig6{w=0k?%b!4qWOe2w!#)U%&09pluOgJ?^0+fb$YofO zg=R=-YjIBzBK4bmMU5M;4aL^>$ zDbnQ7!@GBME=7F{8t3qrCEO@#YLA95++Nj3`N{@WT#=z0oL0DRz&{lQv9cC%1NCbp z*VFAPc<~|RCkLqx7y}%~XLC@+<;B%Q>R|MDys5OPnuK)j<9lCF8d&(3Q%BW983-1X z`Hye1WchSn5>peL_Xx+2i@PnSOXOyN%|ELKhU^Ty#MjcJ)vTgVB@gzSeYlQ?zL1y~ zQK6-v$vz+liwY^@S<;0oKVVOy7d?v`QqjU>Rlh^8Mh|)u2+-u?n?(Mj>)AJw8UC1Y$A?R3sfBfV?JK#4=l@63iu=1w;Qdlhdme* zzj61I%uOB2h7+EoD2|LiKR}f@3_aX^)@FF)9)XREew@~1S8z_1Adp`vcX8D_!;{oo z!;|N|FnfxqjbJBFVR$koHiADY>B!g!9X+a)n-4@?gW&e$K)VhmVi2dEk+mzMA{a;> z_e_~37jCy9e)Q0$?@xeQC99Rp6*9lV`VlA0B@L{hs8-7r_=3Ypf7LvqIfyARp3~Fv zM-_n|NWvywevkTPQImE*(;qHbK!Ubb`9%jHOAPHvk$Vo#^HA z`nbpq)D|YG&Vyzq)9llv!bTgC#-+l%#m>lYP(QH;8|;(sFoc{zs%rCquMVlv%!JG<{Hy}VO!`K#* zge&eKsU*h6Kd=>D!xvqGT2&dL*(-uNuYktS9g5ctv!z|uz+>0m{ZmeG;~D|=k?p! z#GOwKXThlmC&U-%cr^l+fRBGOv^fK)bJl$U0Z|770pa?e>6m{h*&~y4Ffp*^9>t$q7<%)Yo}wk3F6$Az+Vv_p0n_=5Njw6W;vUtT zX&TZr?7QuHDWy9EM%Bz-zPhe_t9+!*uUaBB>ZUF8NRBn zF-pCG=L9u=m5IW6H_^zxLoB6_Dm^oNH}3fjF#%Ffc1Dtz0cmI6G%@3CZM3)e4)dm2 zS$kmXiA~a66gMkdpvl)?g}!Tl$B~1&Vm>eUw)7qlcQ&9cExr&DmngeT16>rVW#)#8 zXZ>d3`8Ju1jEjUHGJvdDiXGM1m)TF3@jt|XC?98IzUN*fuy^$j? z6A2mJ2Aun4;H^(LV(Qs*@_OLrw>7QZv?+&wg&3N~O|7KV&*@JE2vcn|0osoE8M(cQ|KEZb427Yj^-JTRpYLrd=ZtRBvVCO6=|EIB%9K-;{+q z*m%nKd9e9v^gXiq8uXpg_~-71d5QuvY5WU!24Qo%rL)$StgZju)I+YA|erFq502iPAbdAQX=C4R@p58(LWAzS zP*10IYwDH6p}ESu>g~f(sju*pRhc=%A#_@8fld=@UzTG>yV^a$x^=lwPJ1E-d8w<~ zo0#yc`BL&e%peQgSn(NpBX@SbS^XL8VSi$YvVx#fIRzSRXVgbk`!0a9lo7%_Ec1kop#JdZixt!qcH@W&xl~?IuM>}jGZpm$ zujoC~QHWVImrO01BbhtSa*SW#^VVW%cn2XVNhvF>wIyXdCuQE!%NG(D61GiBp1s2i zvsx<*yjsM0<{=L1-Qjm8Un88rBpv6v(VV%y1Y+tvw9iOMtA~u~02L74-~}}t-@afp ze0&h`;Mj=+8R6SQn$+HAx~s2jz`A-I5V8iOF}hf<5Uc9gIJfa1ThLo1w523X?%B#r zzi*CiBhkEDZt1-ZcWY&lg5U6m`hlvxEq5C@j&&P2i2~)pF1H;Z^}FfS`@ObaXN4QWsG+?$kOG2?I$+$$E*wIy#cl!03YFHAw-U&e z-Wp0ZK04v_1jTJFq3fYbW07eiXZ3FS`M&3&fWoc3(8rCHN}kN{Wl(}Hzfjb}fmfB7 zO8d}Y4rY7g*)lSXdTVt8@ceQdF}Aj|J$~mx7E7A_0Bv7Mh)y#qof^H`1`@xpJ%?%c zNQyrGX|lDJ(sQUXEx#R<4c!;7B5V=lHZN}P=-a;bI`Au{D#3*se|+X;F7CMDjbV%M zRr8{QPt5^#CwG0+?{bjvSA+g~2p*AbMOCzztwC4(VvD)v$!eA!$fbi(F9Jj%p?~h2 zr{YX(m(+Ht(z~TEQUwm1buJw6%7m(O?}0yh^3Hv>9H zzDR8*{1|7~;yd92_>0=^5;RLbf4UwEFScPHfTEANKv9f4#7)r;M~FCGNrM^7GW8-J zdtc9_OVnQWiYtEgrxcx1Vza!kT`CQeH7D%KiekbA6{1rrVdFumBc+)awo{8N&#|eK zq<&V}VewDn4euDKP7yi-(%5P=AZNrDtl6dF1A`eSRh#%SZuVma6|)TO<&lbR7|y=9 z9Bb$at4k;fn>FGtTE#JRhhT_SVV*3c^;+d(vre@$R=1u%t(no?^*1Jk9O2GM8kg~_ zO!8>`b6!=KRtk$~n7WH|q0Diu)9}V%HK|k==n_u6)v%>SqLeCr-&a|aJ z2mpNJrIB8@0<{HePvF9?sNeT+Z1y`9UM(tu^ac8~;LcUJCbi=A2d=dyMDE<87bIaW znPBv;wh`jlG9+VNM-Py5?U5}POYr(7b3gt~nB~e%Bc$}X-zt1wf4O!3qoR}kpB0_- z|7FkV_-UHJ;P}4{ELA4P6{yFh)ug25N5@9#hQ}s%l@Y1s)u6x8D>AVtG1b(waMZG} zC_1_$ASyAjFtHubP>oE=$TLtk$}`Hy4NK3Ky^oe)uKR+@2pnoWa_(o;o7kQPFp@J&h# z3Z{e1xAqgH7v&`@*+3rG%8gF+27UHl$Q`Bfa{ebWGMU+v)3*{*BZsr0{9+Wz$qe7^Mm_Hae|{Qj4R>pv@PO>C|H#c=hn z+ruwz3=cm|t>Qo76Z3!GE^Pdl$k@bH)WOc~(;!IB%HHhL+{*pajP$?d#wluc3TU6s zqpA7^T%%E%dHEt=5*}8Rg~SURV2E+0X;7`C-aI?94-+0_sx*=Xw;g&I$*22?w&GYO zE`BxKeWN03W##2$on)=6TQ%tF`T(zq{SA*(u7qwHZKyUtwb0x+(SXp&w>>&bl`US2 z19St zwk^6LTv8;knrD)kO58kB-D&v}VbWOPj;;e=5C$+R{Wb{LxfMMeR@{frJQj8iRO*N` zc0BLQe{+JT?K8#VYXob%fI{3ubvpX$8aAoXy%-OoiPy@!cj4S>cAWEA(O963>&B6Q z*pFDOclcOz()i)C?9ghA?W;mf)X38a=;CrAFN>$^YTv@s3urFVsjfkM&L%^(wm3_5JUPdb1J{D_HX9a zH2cBpe6&o6%3Wp>13XG#QZSD?l7-R4FKyDv2+%5b>sN_~o7GxCUL|Cp(ds3FonVvd z2lSxU0Q`=JiR8kG)5G#A_zE5pEMm?FP!a;VU+>h1-H54MY_YZ(NDleGJ8h>5MF$R2 zWq}o~DI$g2uu3VvV2iKy(fxsNys}EH(#St_%V{T!XGri3=bn*ZVhk_hGlnAb%BnC; zVaV6(pMZ+|g@M0z<{TF!w~Tf4+V$HE$qU&*X^Y;=(?D8=EJ>gAA%)LDESr{czCxnDg6_xQp5TzXc;ZtfX;hoW z(V?~0Uhvq*m;aM+{1r7U24-=9&uBUNy#7s*`d5(sEm{3+b-U?Lu-jRXtdl;&UZmXlftss&4#_1nV&>`e7Xi>4? zBU}5%ExXF}nj!gB8NCaeaY`$KRX5VhM5fIn5gd)vlkWBTWMcE+qS};_3ObA^k@=lN zuM`xaa1ZUe@f6os0^;KY5ox`M-J41IJ6T{xHca7Bkf+41$p<1R(lfT^>nbzY*HvtJ^EW(qWBf50$&{qp zufU%2q7SV`mkfsut!A=s@3J<%lf_&Yyf?k zh)d!U@0HG{2VaY++89*l%5jmoi!Xge7GdW4YfubC4<=WJp(||Ykwf8!?uh~ce$lv+ z;}c&KjuwL6kKMq_hWw)n?Q0Nr^Jb;d`{{@ZBCk;Jc`D z+!ApjU9NlB4x+o~__NKJxv6~oLQ1jKNUOy%9uFAI5957Soq2JxKLAVwLWGHCOAbo{ zOBV^I8y>1l%QdI^yG5>Gtoq_OldQ7rU@GBLjxtjuH!R3Vt(`cnj|F)mT zsIv+ud`|x$2oR9Jtl0l;KmE_?|BrdE^2ssTTYUcNX!L0V`QJ9(zf^@kH%s()^HwvX zb&*m=UDdTMvUozPyEHSSRCXy1|Y*Wq< zu9oDr=Fur3j8HM+?zPjjGi~8zX%e+)FVU8+y##fg86^{OJbEVHURpKC+_#Bww~_o! znA%2Kh7!=^+8~*wf}`x6@80+=t?k}A%wzV&Q*|TV|eQ-sqCjKNir%#+s^@-+7Mk75R$c} ze{0d1b+%v{XmBC(qV=d+Voo zsko{QpR#VVl9Tdka^xjxiQ(OIB54YQStIx6-gAoMqwkRKcVWK9N|uh7AIItvHptzC zfYjmd@-IU;W0OSz4wq;}Iwx&e6-~M7dIr(O?VEP&k2QYz6(~)UUhE4RNAd=5PO8%_ z{~HaRNCXAvhA>{-JKnw~d@%h9=3lq9BT|GL$xq}g`#InL2Qc`zx&FDVyV-qu(0|%! zoBh{1|Bv-OC1G3!j2S&d;f1xJp;6n8_N4csUJYt7B``dYskx@;)fE?zkRisxdScT; z(|q;Cmx@_h7K1)eYi%!k?R6dP=KcBwatnSO6?TcmXjOb&JgA%dFtC_E@Fg!mfv6Nq z3B~)5suPNPTqt;mEVnthS`M6hCXf^W>56VubTIl|LbR-T_|Ta6*H!RVe;Uo5i1;AN zZD6=h8cS>`Hr`MOY+ZW9-3hlL5_MX>?A8FCw54Tfmo9RBn&&G3oF>nIC zU-z!rvu(2%a>Dv&e>7*y56KhRDFdh93pw3?5!z3kqUPW@N0}{?4@TRrv6A>Ad~5 z>S4dR+;O#uWdJ!9+cmlrxT-#t7(X4s3U_@kOm@OuY4r3Z{;{_k_Ljcv#4W2(FK8aky{|ypVOp@IvMQ0XU-qISJ z)PJ7I)D8V3b*J%Qs;+cbn*`WYamHH_V^c}JC{_P}^Lcnj3mJ`~;-bOEqDKD$ZD z=2FPs?12^KB8gB8IN#xXq&GbI6>8P22YPrCmBh#j3*b`8H`M7VlYa4 zpIahc7sw@$L8h3o0M_^Cn&Y)2#S=fIcDJ6&+w|U5{=^zT2O?07$#kaB*R2vt#~cG_ zYsv)#brDA8Q)*IEu!cX+bRzw#m+ia!`^o1)?e0exYOTdQ9xW%b_KWTjIK9${Crm{w zs*}B_X%&s);F+{^AhhwGM5j?b@caw;1jM2LBQYte8gu1 zOIJJ48MtQ#WO*40+HJRslF};Ipi;kvf^rxZou&I%_E>c?!~sHr0ES537`joX*e@~N zKU);tR~y}vU*U=Piwv>6(QSe55E>?7fxn*W0~uUtvHRnNc6T_;Sgals(g}kDWF6PC za$V*f=B@QARf-4b*OlZ))%4Ce^ycN*ldkFKhz?o&H}m?uqv?EbCu_VWX*>}p;m*!D z)coj<3CDkzqWvtOu(MeUKXtlSA5}MrDzQ&+l9Ozm4V5Lj5Ty`Hki5Nc%d97INv0HG`G3JRjS4r!yi#jEpiRI-O?`P^ZrJrK@Q*6@!=q#iXX%A(y# zEtG_tTF>ee8e;(FQoND{m$k0Kig&axszzqS5kXekRaM}l=99ryXK)v636Msu2kI%a zTaBnr1J0GM)@{s0TMuNhy@HTzsy+)+#KHS|2CIa2gEmM)wDQ-2^GGOj49}kjP=~pm z&JZ=pN%bGa#br~k1HEXi+&i(}t=`9O8G?Pt+EIg8juvxMCAeeIh|Npz}Uw2`$03zq>JX}1}5wZj8Gw1Ymc zsG*5M(MAmylsFghwzMhuEX~FmleZt=CpL7rk%Z|Q1Yx!6 z3T$EbrvcPb(+AYS1@n2-72)b=>H_D|?b!>m#M9x=Urn7r)pm$&(UEppuA!}g1(xV> zd2yCJN&`2wSg#;R#%;2E;q;96UmN-Ngl+wBw3>)=CReDu?*2@((GYe6w5a+LQu5Mj ztee@qA!oX^bXj6XG;n7%e{sj|5uxdBa0RiGW0MHmD403aCh&j#CW5Mvc&}ho=ZUMg zqjeW~*p63m+hE}^6~}1!-4>1OJ02)r*pN4Flaj1J!F)n0P(< zN}tO#uJ3_xVs4OSQa&f>28y}gu9C5-j<1pf^S5ceC}@kbu8ldu1he+Lm`w_s)9|Ig zVVa!{Nzd(T9{E(Z<}RvVz0+~LXmj2_+vem>v&SfScc+QQS=jqqQGljl#CF54qxoc- zJ93v-l5D{W*Da>}i5a(=%X&L9=uBU6Ir>_%N5?UlA3IYkFcUA4TvRlTZFOTrK}LnJ zV|V>n$!a;OR6|^l+mYiS;z~d%_(J*PMi;pXz$DZj$-curva(41;IM`1gow5ykB@c8 zOwF(r>Ckx! z=cj}-;Qitc^`@}O{KiA}cM|rm{CpSZUS#GIu&sXP=bZol3Ch2xCV%mGvx?~c7Yox$ zJlGB@R}f-j92+Ab!c-(&Ksp9P7SWwSmY-TP4Tb07f_+52SY6)}`mdG^Oy{sC?J~KS z3ZL>i4zq8w4%dA2R~)*!d?6IO8-vl!$?tA7kPgJgWRYvW8llLN5JqXH#_zq7Wru6- zU$LWVzn)|T#RFrytNGzX3$E#K$jnPa}%LUwJQe9;h%TUrIcAw1y|ZEd_lUE zaM4}QXdlUA30Dh{{Gq>JMGVb1ZzwK35Fqe=*eJ#~1lb9Zsnn6~h~T4p;;d|sRJ9NoCh zRdniy+gzwc`p70rg`|a5P)Skbx?|Z(W6vtdET(^~%BWO;->#-Z96#odc;>(~_+2Hf-DaiG-hfG9 zQ4~aq3HFI9kM;FYWA4nnD&`Qo|Q@ybGA-&hQ9w=t$_QDfD+5+}yhYdUVLANET z!{sw(znM5$)%Uj8Ebhss7hmcyA}A2~5kT~Nj!xTucZaQH(~y$;Mf?yEj176b4oo@Y z4V6j-0||A)^93yx%e$2%k)3Mg^NW1q4)!>MkC;4a{oc$v3(!WJNZ5P5SVq}KpOI$O zX50~b0}DE%{C$>2m$i2ZDSY`jYbb4<^(9(3heJuK2L zB`An9PVp2pai1B%Ep(8G1X9B%GwENDW;(nab{wY3NV6 zWP>XMT`7z>8Z7_sf?ETNy)k&4t&Q#c8L%iK|#2v{CO2 zBV(XbOxE^Ie$gRpYKD%x47oj)hMZ3Ij>O5mswhd3m^Usf%*un*8;0jGELTSDY1CUtqr4B$fGATyOge-FL6 zVM0&kEXZ)l$2w68OyTUX@pi_)c|RlePg)jzvLkAG_NLjDktRel8&$J!(9$yD#YmWl|cMH<0N)aLF` zU=}n3nI0!+dzj|YS3%}xzoyzrn!apvU_~0Sty{B({zUj9O38?MY45{eaHt;g@F!-V z;mdq2EwdO=FXD@4XgoSXo|_;`}cKsnZT6qZ-;5I+gd*Fb>>jN&7?a#TYQ3y=VE2Ge&LUFv6ACAsi?3nzwV z9$9@;>Fvb^9}<$@&gXXPd$uhzuDBkM47m8;jd4Snq+6G6hAohtLL;g@E_+2u-GaW3 zWj_^t5~8EtqtdZ2zndpG_hZ1=rOmB~TM{Wb-w+p&Xf7%AFITrFqN=H%NHH=%>EacB zJ&sD}NF@*iS>;xqhawVG8821C=t~hg#Fha2Wg_*;6QwqA86C`=Lm&0+rVl;B1k+mc z&17PSP0cHU57pS#+=;*9?cbv3Ep2SZ0b3^WjslAez4yX zQYM(o5}t!?@zE*$I>t2w@Eij@hIoO2wP;AnlJGd@$5}W!Ny`+@CU^%JL zDELdM`I8VO?%i2VRi(=)xo)=jz23DvX4^mQUK#{|U9oh+m@wLxHDgHN*}EGene#A3 zaTc*thPi?}7zqTfYR2~wV0e&v;$4y} zB>Bj5SCrJKF2SnuUg9>YDNeDsRBSG)h%Yj!u=WxtPcfV9(XG?-i1g&G%x}*Wm+G|4 zMW14;+aG~?Shgn7RzZ*c@=HDhWS5mFsW75r|L)k}%!=nF)lwSb3YC-)u1Cq9s2JiU zDHx5fztFs+fyPq@G)v{YDcUEwPSi#{*KadWb7?88xI33-63_vC%vz%58dZZ0x3-qKm^MkhAAeUm(sx zySNMe?!5zsfXbtMY2##TD$N-Ew4&sg-o~K>S!sw1B zLY|#(MD$SZX%G}7iilPipwdxdyrl+W{6XHsxWOMBPj*BWnPadmfv_&kSkhL@<~EK&J4Om9+zsJ{!0 z8Cdt$#XYmOO>omRXvWGK)1L2Q7y1QeZ%)U89NI(_E22(LaeSbkmqU~J52UJr(-N|` z#Ks4n4lYjTZ85vgLes7hWyrh*c5m_2a}?&h-7YGK*+@q23I}stkov>x9f>l!a0@Yr z?m34nfRpMQiv^;HhF|4G6|CVLj?i*R`yR}Nb$n0O5Ig4EALAJSI+-b>i_rDRY4 z%js1Qx~?tk?*^P9n83oHf$gy7;H(obnkvq*=6Cqpo-x0in7p>fv!7KU^9_DRjXtdes@WZ8? zdP3}WFCSreoVmp{YA^PA7&t1!bDpG(_Cu{7w;L1My*M&X`@p5LqTs{^rLqh2@ux0a zP4)OivUV5u>diNKZ>+agB$Bttello-WIbQj!VJwp`=2RI1xc(m{Te-nZmH#E=(H|T z&y2?V^6}ZG&+_T{?B`mX?|&;${wo)lT_l4q{v>b#|EY-mpU)-#FDzk-vff{cSpGV# zI(K>b`ky-<(bN*u_UHy=B$h(xfv^dDPaM*r=R@Y|=9J_C`GNq25P>JKmx4$SjxQ*1 zR_=rozuFG7NBKS8-~Rl8-$FLyjFm`%%k>>!&^9>GOBsZ%~{ zCwN6RZ@-CU0L|C-l`?ItE_VxUI){UewjYLvG}oPeL9er{O;xWoD2s5CWRnF_4UTJu z372>=q6%{+3X@(uwwx>r6ts@;Ch+w6R#43yNWhP`Ao3^U9BkZ`sy$N3c46F`h-(LR zDu!<7ulVk5dLcVuK++c!!JewnPK5R9Uhk=;jQL98DebF}MPJqQfrPG~n4b5wt_QPL zFsr_Y$;W743wZ#G>Sd`rck!2CT+)RXL_@YMU(}e;_4QiM`63w*p51WMut$<4ji}^F zTFAY78P3u|Oe{z=_*)@@O_|Lf0(zdMe*`TjoBDnHKtey10DpRdZm#E`D{Kx|pk^@Q z2Ih}r(Yct>`HLJy1DCsiQKY?6d@<^^si~F4ZwS^%BW6doMici5lyu1c6kPs(27pHkS>@J|Fa@LSxtg3B0jatC8lGQ3K!@V;jVRb~;Rx8|h zytsaq^kT&QjAX}Pv^*O49m=44+|PrL!RWqY^5lunSn8=&uuREz)g20k zkrT07XY40#*-k?zxEL|nhqB5D4P~Hut&Lx8gWa9Rb8Y4;e&nmhx1o3q2(na@v+wGQ1l5etudXvwSZ^#F! zQpewnmq!GxxYX)AXY{q0X@Jz_#@R_IaJzSF4JYYpbvxdY^9x&b0NIpR9R*NO8OZ~T zMP`aDWxJ4zBTJ8@dT6BN5&+}@2yuv%+x*hwHO(XgT5!+MU}HzrX$A!gQ5l{&)ab|~GZ%YjAS zBGS-in+6zTuUl*GA2u|DSnkGJ&E>!YPUHfO6CA6%4YVhe`gvn{UM8$2G3 zf7NafSM@H|6ZxRaY{y{;70$jlWN{VUPl1C!gn+v#LpLhD+I83IcDX_zic&fRM%RoJ z0v?Zl3>=St5Zt*~V{PHrER=nP`bmNb_0bRAT2k!`iCGJ}Y5$QgL&U72ghd`3TT_ik`PVo%J6&>8X`jzHF1baJMqqSCWfdA6Tws4+(k!y2)amaGvfIWy(>-n#Cl-W7R`k6QqflR(a)9``;#-m z82pYO**a2G*fD_oPJL}DWv59?x>p}DXg_JCX+RD&&=ST(^?4oMNPZxf(m%DM(F( z)%6!{^mi^lha8?V`eA&u*6nrgij7U5>|?c*B4T~}SmCj18}Zs?N+IcXc^ zAR-QNfS2b2szPPN3JzVsY+tMV{M~Bqe zVVRm3+I0x(IeGmsr*+<;l=(FL%cPu+j^7kz?v9Zq&3{SA)9{VB4wxBBPv$0wAw}ut z@l!y=5+(k&W{%T>vud39epi>m-vkZ@|W~;z+tU8||3mK>g?Q&^Py+z_vv!p82k&rv# z0fAEhJ^QG64Y#z}I~siymzBki6Ux<^;gANdx6?{?DBhucHx)k1Xc0m}&Wt58w?R*h z(wJs-4)kA>oTYD5lE6a*sTaAGLCd|6$hAM#ziK73tN45I(O*z2N|@c&_Y-QteL^js z|ICO#8?{@TnYey_{IhfW-!|TVQ&9d&lvU^zLJygQ02lKWRP4(?>juX~bK50Vil)sc z!+sRyO=Y$Vg9n58kkO!Ec>D5BwToWHyd<_ucX6D>y?N&jaJXcw26?E}5yHgtvOTCx zk)#eg$9IQbMni%1laSJ|@d%bvY0auxLnZDagw(6D*IMM9(3a&H>oSoMyImSP%Em^H z)mHXuEKWalS-lQfSHJneyCRiCOaGKh9rQiKzTQS9l+?u8O-}Rv$->fic2OiWIL5m2 zzFT7KLF;Ilpi=B8<7gu8hYC^*S~r7M~`}AfjZyL-2kfoQH}ejPJ)v zuyKIQe9Qw37C}|zQl#sR`KdmQ>|^sh0qkZ206|l2;|f>3gCM$K&5DVTIbg^Jp|>Xh zF~*TA=$8kScI_sYDwD;9ATEyLoe^LnGs7-9dg7cvD0@s47DA;C&4mCCfLZ*dAPUVF zW|UbsZu?IA#0iq#PjuGcNCxz0w)kkoku~Vg3~^eRl4lRf()+*Zng1G7x$Y;MtiHBUQQ|8*l#v+p z2JW)=K%sCMV-YfIk=e&DkXh!-cJ65dT{{6=z_g!FhQ1GyIG1#Ia&VAnqUk<|6D@}m z{2mX7)ef6q=C1i5z!a31rer~1y{U1iP91?lRX33Y8fv(BjrLQkgYJ0X|_^gjCV*Tw6`x^ z`|*t>p_d&ktR#~QlscnD#>^Ox7c!f<{cV%kz&MAqzoxE?G_>R1n%Pz|?qKOWnqV=h zRiN)85~>iYwMB>(ePKF@4ARd&S)9laU-wjuH_07S3s(R&rFzR?Xj^Lb=bSL2F6Cwx zSWO7s9V;-nGs9H2tNF_tWj7`V&i#bR1H#!9Mweolg= zKC(mMl`PC|zs+Hsp`m*FP4$-E_zr9)u85o zs9U3efQ)?#Q2*bQ+&?DkKPfqFA46TU6hRApkAs6odC^&SSUXW7wm9ioOx%^bj8xDN ziXupD5wAOn7U|+&W5F#+0AYO~Xk@!?(FzF?O37EM$zWt*B{6Yij1)Z$r)j+fJu?q+ zb<8REfWtP{B(Jr^9zo|WpRP;aLqGq`XMo@J(Cj4Yw6Q;lSjU~<%~KNJM(SV=yEmoS zhit&~u)^g@`m^A#m1F*xjYa9SYp`GN8E>?I?=qW#CTEP>Wnf>@DMJ9M1_|LOhE_^GOoAm<{rIjbTAj!fZm^l!RtabpB+i z+UM~CXOBIqk3419FPX))pYlu?h;q{&ly$W}ND4VZk4Zam}1;w~3NvRX>?AblQ&MtaYeJvJ{?^7W{ z0&uqQxafpS2jpOF5-7m;{{p&<721)nDw~hYc=D2E`$advWl*%q8T6|zqc+%uyQ^m= z@ms?*vUwC&6tddb_%hF;v%7e+SrxCm@aAe%<6MFUxv6`QSYeFW_)0H)83!|;8A)mb zi^$^q>|s>Zlukj8KYa>W$C~M$;WHNcuFAGBW&BWS2-_g;vtwQ+2;;}OS6$^QU}D~0 z++$Q@tU|IpJC(%NW~?r1LAMgW;HtuA&z-MP0XaErPM3;p8FA6jIy1l;u^Xpy>$Nc` zBc3*&R?j29uO;;(XbVNsoozZ7&`4g84tctJAZ<)Gzc8EMxQtS_)zv*>$@f!xe6O-< zd1Ox~=jit*2{^y9xoSi{i6Iicl6=HwqVvBx`wFNkx3y~l0V(P3?gkO1yQLdt)0=MT zmhSG7?(XhT8j&tZrMu+ce#djwt@qqB{+F@GhA~)ku6S2H>sj-8Z=l>Z8Phg3LNk?k zLR!b|fps*k(K4U&o0!v(4G&A4u#bsXmjb7x45}1(4zl^glQ;rQ zSkI{@s#^1`4@I`JUfQxL&idlLj7l5fLU*1~Gf9+IUksg@?z5j}B3M3kdz-&$JgxhR zs(K-NRZ&@yEk!Dikv6%ypAN+-dW52xn@=_3q$mWZk+P)>$3o2cbwctxOLI3Pwjk?k zJynA4Pa{GfFAtBUg{7ZpJmd&g|9G|i!6>(4_0w%qT<&VZ5_O_oy2Uw3;OfEv$Hf_G zcUqCqDt-`8OIoyqqsE{NDtC1@)=H_?8!~pK^tT`K3K)}JUTgV!h4C7d5DVR@^2Eg+ zAdRReMZ7zis`(;ynoj|t!zlv2ro6+c)EHQ#o|>Kn^S*~CH!GDN#!C0}JlQpA6U$|c z(|rpQ#~!f?)6%GckiD!qA>5OvasOmbN+b$EUu<_?{-Q@uH9xpPORCu`j=10NAvgBm zlh!ifCMbK9R>St`7M}?vcSnyE3mRVXzO-Yt)+cBrKzLq&DiADxaR{Jl` z&hl53hQBz7m@ELth8GOS_$%JP62|r$Lj>x>qyy)k11RL&un9T$pELx##y-jZvUmN++zJ7GNwmcz9*>1e@(>G{8(U z;ouYW*+PyXX@N|L^p}~weW5i4NdZ2C!@b+y&@>=o#6ewCRil$>&^rVxU6|$0*PBTb zsWQpFwn8Ru*wLhlph}zI$91cJdMND{(fMlgIcM8UrrH&s?*a42bY2cbJ_e1=6sysQ zEy@(AK^V_B7dW7O`6JR3mDsl6$axi&s?L>woZY-8-|{|Wgu9usm`^nZ4`zti{g>+6 zv5oEO4#bK#5?|KpwzX!`nW`nRVz|xds$h@YHcZ#b*IYI=T%r0B$HALuTJ^05DaXxD ztHce=n(0~buutxceWYbiD#8oQb5vz4cvW#IT-_h!*B60%`;?q~GZdeLW6`Xi67M3q z)lD0#zsV{gT7AFXCXbT%&O;G?gm9gzy@sQNuIXcf=F>Mbu6YCEjefC^c~|^MQ-mo? zAge&9+nAh2t0=bpR-zW`2PSL+uMytH2e4zz&@x(M0-Aw)yqJP7uC*!`jBd8bOp-N)Z>#F!%CAka2Fv!V_EFDg_G5cL& zrQqqwxkR~LwAOwjrQ>7u0;<6fcI?5a7@5-xi;&=jU~QAgWkiYttk73iJBW+|@$a$< zK0u;;AP8}PbepMdj}zPK{9>foW(zws=<3n-(^HP&eHk%!{)DqZs|Ul{Zi?9nkjnf8~ZcTQr;<&zX*EHI(2y zpSLGFQ+$qkHAs#vsn-PYis+i-t98Ee0atOQP+6+T#^lCh-vi0aUQDRb(N#0dt3&_U zIfdhTelc&rgg@*{o$aiS>2uMt2HfEIm;Q57xQpOAD7g|-dm2w z)^oVyaCzuS7D6r=B~7uys@5l6-5j;GmVS9z^cQd3$vM(?NZKi^0C`s9p;VskANfW4 zi9ZE&e+@?WH`x@VBhJ;>t8#Gs3}}OzR1vmc6HJB}svz#M^Ea_nA|b%Zb|z^cczA-@ z*;Uc*HjR=tg=0-aFIXvHQd@!5mtYkzrxhk^tg93@XP=#Yb~Tx!i+>JVWnWGFc1~Cs zZuglyadwqLFp2!xat{^?Cv?vjYh6Dq7nIq;F8av$f||D(xz1d`U`1)A{bJ%=7#29xOiIVGUk! z=93k#eV>aEnKQq`kaOK=kD6r9x|b(y66rXma@iF1tRg>V|1Fb?4}(j1(@y`CaC>&z zDSq%ob4^J2gk`z_Yr0q~Pt2OOC|p^V^p&!dE&gmvnqo`HxivA;A!bd2RiGk90 zP1r8bhg_2Hfn)EiRkK2b^9AP!nle_9>lBh-K{!0pbSRi6`Q$%g#d+%P^1vR?-ufH{ zFgt%rvI#l$?!~0q(PsWLk2s?2fZQqGHW|XTy~o#hrdH4HquS&mGNrbT@lR>o_Hz5# zQQj7$4-~7yu6mJGSUyF1Ce2EYSw4N8PVs6F2jyvl1Agq&NM9{Yta?jF<5gNcn@0?m zuK|@2>Dj0O&^MWlR46nJtTMw|yPBUfEKZ&A@8Z5nE%VY0O5I222~a3$$r73CtY%r< z+zl+xkdxO$7uGI8peL(l^hK-qutbq+x?gUm)ar9{!!A*6oVWt9^lBdIli#*Nqt5#` zXnQ>+koO|!auIz-ciaw_4)fI+Vq=;k*qtv~<^(n4Mt2oU-px0?bAc|oP;XO(fbF9* zDQ|HFd*Hi!D&-Rc@{M;q`Kgx{*Wv4^2S$fb$;ZQQYs5M{8CR-tE1Jp(X&O8d<;+S) z*FHb8@S+#nv2WN9rFzwREQZd)uonReD6kjl)x9YLs75u%K+9em9b)0emwA#^#Tg(? zLm*{0OJ@ZP`~WS2=l*&!)p`s`U#tN7-Q&qGl^f{62@y-gk5y~I(OyE{~#uPuxf4U7I0IIKnK1YY1T&BMiPYqU0$kH;F)N6>(ph)aT0oroRN3^GG<+;R1E-r6exBGbn_*b>IRlYLa zrMAClUrE@mWoRMHoa}(p6;*=@r8E?dynFD}6QTF17uFW^HPOV0Y%*-av(V}K$T@!b z=Of-8$U@~0IHRBxJO-X%TmpHd<9fTj`iKN>Lr&2)>%873$K;H}LNC3{BPQJtzU66! zlsF?Hc`^1P(>o#laPL&9#)wb-95F{CNpd{ZXMFs`ScBK|vAl!99i-jkh^;D~m% zLW{FELpf>H2b>0m*ecNypVQIED>JX&&t5@X4~W{K$8H}BoLFstSjurYJ*e^aD9^6M zn-dwcyhC%n9SD$sIki##3eO|a+WL4IUc00lK!dftass0{K`^Fv+TOb@aCh*a3VEVE zPtcK)hg_Ao$SjDbd2W8Y`1E~Cz#Lgz8>Otro06~g%xFZp`{%*w_7Ioz>Dg)B7@`5> zqHXlmZOzv1?Z~>fhcu<833axKdW@*h42>Uoiutti_$Vi_2bC|Z$n?bn)_d!L@OQY@ zcaIt0ws-8m9+zkdqiK&2TY;+C^Mu}8j+7LyvgU>6zRP^}jQ0|&;e4f4r?VEjZN6w_ zE}#`%x$K(e&6kmmvn&Q}VyW&krXHE&1tlVpg(aB`CnTKS`8CdEVd&PAiPY+m2ohh? zC->=JyA7-BD~=qOZ!^lGd2=PdLfwrUvL}$>es!UD9WPX&Z+J~0-YSd%%XhQFcd)K} zQ?Ltb%k8|~j&X6HCyn_*Hv3_jR`$CjoEQ#Y^HAE*&n)Nq3%)iFa9gq0NgQALAFcDH zG4(q^ZyEzy(AHIO%KKa>``mHLJ1^lf0?zeYuuf+HEkyQONjm}FWA{+MT5Htgf#EqI z(;_YCfR_E=m=*<%Kv(SlI-a-t%XCv3=lNn%0H?owCd|R!7YUq(H9!af)O>PrsNO$H)=yID ze?n|i4xCm!caO(WtTzYT)Z_M1uP^2gAr4~JiyfwNlvr`m#a|?<5mX_@FVMXnTByz? z!i)JT-8>lCdeT+j5@6*vkEy7G-gT@>uw<^8lMG8Pt_LIOq(6QZ>(JJ@tX6`Q;p#Cw zz`~*7(z|RsxteWhd6~|*E3Kr32)93NXiFG_=w22`qO1+TF&Qi^;Mh?@($X2w(d)$? z?~K1W@V_$FuG|mipEme9q}!-vadea&rL1lEpnDEAoI70`QB!a>DW;sP`=dfUiwXXI zom%bwY!#>fb2lTyC=G+ixNZQz!iV9e?Fo*DF-v@jF3QYUx|0|i%HIZz zW>#(uL|HU!g!9Nf@TRB?O^?BW&qCmkVx~N*d@m*VvP;S@s|FWdLk>C=H5CaL*#uft zeE)fLivL$!xFo=6K2(Rx1BM#y`3~(zu@l_U7}2BadVwz=n|+{|z6_3Sdole9(Js;` zOrl6LrKEPiS>w`VK#P(Xmp>QZRks5XgB4(2tOb$d(`4SMNQLH6{2_0s?KzW-%L|L&fk zv?uq?&vvq0C%(31LmET2lWs4*3gZY}L@z8T$_oz0_uk);QM#`A{l4C*f*a($6j`Ln z8WbqR-1AAyEDOk6kS13e_eHv$@#aMaar*=1%4c5mG(ZCxB#l_nr^VUXfXDGZ&Peb> zUCM7XL?!ybPD@ZASWX>*7AOh(qg(u{$Pah^rc-8>3ThR|HXiYBM-GEAd&Vw z{qy_SIvhpLb^tqKDR+8n+wxqcZ@pW8ttfT-$RZ=rQ?l@wLX#Od+*7343MKz8wRB@x z&V+z0+2#+)#2lqY9r$Gy(>nb{SEX1Nc-g)9M1GF)Ps6AoX7dPJt-I=m`O8nR&Twht zui;e4+t=xxdLH=CU{5&nWfY}~u1&VEr~t5V;ITVd5szIKA9o8m*rc!2u2D{d0;5`7 z-v};x>;z^-oGB-wqY$bAwg&*}iT%&~bx}^FKzaj&(&|Py2TXTv%QpCqnd`Kw_6t1} zP{rE~-jdLxICKfwBf7Wc?UmKWGvxj|C&pq{jWm50_SNKzfF55Ow=I!o#JBED^{44C zxvlCg$_4Fj2)8ABlYvE7Ck>=8_?e{JIR(*kZK4y(7J$@rY-phug3tuJEi?eY;h|_077@2O;okNXBIgmdeIPVt8T4C-X zPM5$Zw246FDKyLHPuB%8#I6~h>E7GZ9@fc@zbyCEP=xUbjq`R|6Du-Ry3oR8w#t{> z_vG7)y6YHE7a{P4%=uufB6-jr_cDwUk=u+x8>3k|hNr$0=v3x`f}XU6>1qyxjP?(H z-Z0-C+T*~kZ=5JQI05%gLaWEGCHmpwzPq-szgA6`Joqu-U85*@B`8!QDabMQ`S0WU z-})*4<{Yq8bx{pp6ysjD3ea7(LH>kB&F2*g8fuVj#gb;F4_WP{ZuI^7CK?kK2(nTBUjG)H#hXB+z7f+ zbOw}?@L38Y6CHYYrC97!Fp_oW(&>qd#Ag|*PI9`2cM{|37^7^4g$|8HP1!r-Re-8nv=~Te>P*J~FK(VtHyS@Kp)cB`#+sq!@;PkLubEU~@gSpSFI`S5x z!-cs1Z7_DKb5q(8r9k23@ha#T5@hk+PE|oIC7BRw8#2vfxeUNz@410RgBI^-Q)p<( zy#1MiP`$b63-s)=bti ztdv5iRp^aT{8;n&%>*@i6D2Su z&f}~SS3|%x*cu|8*Lx7W?}&*f2GOgi8L=i!akBz-{109xcT?3r1G<1t&P$ z1-(8Bwjttb_9NLooO2PL*r@KdPypZC?o8J!#Di$PJv1X!XUn^wM@s(CS|Wmd5JW*V zLr;71+4zK0EeDWrGu9Dc1}@Pe4Bg(8JD-Gw(lD{67Lf||8KQK?P}61g^h@V-nCTc| za#g=^SE;AZPY0}s#zkP~yDrs)JyCj?v3`3=8TlIXGY;0`RhEQO_&iWW_9`Rz_5}*> zMIfLKcRAf5j!-PQItSMK<-AaCRiUZ#apPHT z)0@E)XV^}GSuI@e$q?Px^IbvM=@c7H#={B0=mU>93UPv4q|%fZBzMZ#)4D3kf-_rw z`rU(>rozoO{GJLJj1={K++We=q#`4%g8>^8sB^e&{bUZ$aMHV8>uh5RrBT|;LU(x+ zk74ElwjY&WRvwX$Obie|jGx4!k13nRmw;J<|&OYd}kO9oC0*kvQvC39usYe*K`w>N-Y6Yd5SwAJ5;WU7hYD8rq4YtDt<> zTtUg8TM3gh`osZBAEu=Bf4UfT z^^ALDx!M?lG}u{;L&4ZB^2HBXNr62%Fuqp8&o%tbU#BcGqI$xQQng)X21!MVxPy+# zN53%TVo16rrE%Y+9k?xXv$x;7-9zZ2($gBq%PWBVf`pK-Su(OW{DV^@8FC`M()$=0 zsBE-6K(_+u+b=#<<*c;@!@{GvzB9K`6U?g`K2Kaa_A6BL`^-qcT?pT;_i}g@-l)kV z!KZqVLAcx{ydrdiEtf*73+<(bAjhkZ$|zd3pJNx)P_aD6P0j7LFz27pMwfo%G_qt9 zAF#s-b$;#>`-#3zf7`!%muki=Z|oIY|Hhe0^SG|6j-mwzFF;F~321GlgeT9E$efxW zLL`SGlFI9CpPb5z-O{0%JpGwIeB9J}ScxT;KO#CjrUYs5tMm)ofW|C6({X(0!lFf6 z)7!>KLY(G~T_1x97C!(qRKBSiLBO8$Al`M`BuZ6sGK2co+62)UZwZZmatWML7s<+}%M`CJI#7C^4NaheQFS<>+jnALTva4Q=^nYG88GFOCgoGVQxO+QQ)jf$#L+ER@} zP`dVDA)7ap;CIbkrT%TtSe0DXzn zAHblsv|jGix#n0cf8+<`QnP!3+ojE6*zGQf7o>mi^c>`)p|%s2hT9C12ep(7R4l&+aQd=bbTN zVPX@sk_cN$BMlwGgjJ+%JjH+`7joqgis2Q1nS=tUqIGv9mN9;XI&pII$n)4tur}6s zQhpXOnjwTWIG0wO(|{Q=jk*w(Y(yI8B1vr^p`d{82098^r;X;5=7y_NwoWnYH zZkb*eDEhQKtjYvGxs0-~!7P`^GO7CmLp2vR;k5oGa%Zp0vV$Vx=Hwy+UC|!(Lh<^k zX~wH2Qtg8&nKej_v{>|0cWz?1ag9xDMzVG`?oV*2)LJA%(M zI1P|19PRHT|4(jHV4iPb(K^!IEExVTjOv@NINOgjdsN4F!>~W&49H`^!!s@!TOi_E z&}lrowRm|6b*rEkFGS1ad!9GweA4#fF*SettK|pQ2>nk8K3WHMgxm2^3z1o;>IQFp zlxEFes-}Q^p}5vuyEbefrME-A&Fj|pNw_L?)cmQ?7!vM=I+hPf16wbmS*<*LUm^eT zB8{R36uD!PY0H4DexhDn%X8`h=(B_GaQBX0{;T(P~c7`(&AIcdrg3@-Am&X?hGrSUIvkQ0`n8;J5ypb`r*0+I9w&t=b zWZjgkO(;5cA0c4fIOoJ{5{1fCoG>Hkl?feEZb7OJCA~LiYFy|7YhG*G*`(S8lbauh z^{RQ+xU|5Bt)$k52j)=&&+YiFsU;pHNoTU3|qm{xr@e=^-Z zmNDiDFrE_}uDLE{zni!pCtf{WSj6#xGq+CNNw~4yw;Ofduii>;^=$uo_u>JNC(;@% zNq@?K(k}`%Du!gm$B$AQQ5QEsM0+y^6SbJYQPjt;nCyeUwQP?A9M|C+KR`(cjl?5> zu!uqrqrcz{Y%6M-ej-hxDyK?q>|S!bqL~Yw)rZf)l{#Zcd+~alpjg4%2u)e@!-z9^ zt*ch#1SgKLOkCB2B%j_}gsnd0GUxb=`M#-G+0+0IQ%0rGf-Zh!i}7v84x67^Eo@$H z>5ghQ6DK`m2I4VN_gIOWERci4$B;)-%=KAz49pa-39?PoXcvn)9;H_0mop@y-#uWH{rj*$%&q= zQIZ}<$Nl&AM*JpZE=CQqE%uIfhl%Tg@b)l z48qpT{2_4x$)+KfFa3SsBMR6UT!?^oUQnP&k}DpCd<-pGyxReh__iBCdcYev!WtIO zQv|t-q31ftAUTDeiX}DF-3vv@;3Cs7F%EX|gwNW<7tVcQ(=}(ByLrcn*rV<+bfBPI zMo;G@C*Q|%xjvV5MXXBgG~h?96NyascHjLvB^`Gm@AAydzC9Uc*+>X8Rk6A(l3|vJ z=YY6Oxmh#7^~=W5SN8z6FGpsefbX}j)~LeQFi~CP-|P_f`3SyT0)7gJP;IVkRXSVnPX&W_`QcT$IKKZ@f_lz8ig~MyN(p7 z8}66LoHHg`l37Fzc70|Wt9W1+@=TacokOmtB(=h;Lm>lUz6P4`xryduc31$(cq{sX zI4LfS&VJJrzIcdZBbO3cFgheBzM&qxmHS|Wc;@(rn+SU`*#MV1?noc!x~e)4bypf% zJ8KzTE<>h@htjGHNSDg$PJ`LOXYH{@BGAg24@4nz#4`zc3h720X zUaP`OqC=0fyI~ecS22c@qTAH3d~AqGZ|6Hi&)Nn*{cxYcIrZX06_fpHVtq(zyeVYyl`Lrjp-?3x6hbhjgst%VZFRWKYHUz%(Xp9GY*wHyX?jZ1 z;%kC1^aj(D7L&LR_DVeOudWa}ND1&YzRV2(xFVXusGUr+fLWpgt%wFF?PLbYrRYAh zA!0;;R$_^RZ9SgTDOgrS=iG@1ZPfHfnA=WGCVj@8e)3MtQ$0$)>OC#$Zq&K?E?J2( zJWF3r$v2+2p}ifmTVSzv8Fym%B=K6}^Dg^_ju4Qc4Usoo+3l9~F`48?lk?GD>Qz6X z>k$%F@6+Z|B_X~O>UIsXmw zM?57ByWVMBzFB%c4IUvzTG!N12P;qJEexFUy*Vd0gJJfo+&i#xZ^5?tupZCqpMzE! zLvVjIc>exM{og^>e_3dJmP1!S`6{QS{*g%@1?3bQKjsS4<_1eeQYgL ztWK3qj~YlT5Lt$!fTGWniZ2)$kXo&ksqW$(dAap2HHmvUDJd<9xBaWz&0~^aL)7$0sVICXRs-?f)7n#!B5+Og`K-VQK*XdhaU#KET% zM!;s+w=6H-ls4oQkFLv!Qm`!!E=VN|+e5Vl^=$azibaC;cHj+YyxHc~`Ve7Z-NlIUfOsQa-g7Wox# zOMwnS*=y7`kt?==mGTV&-^3@5??a#E9tH0(RN}7eM2-2=qq&)29%^@C1?Q_og#psTqs4oi?W>0jekGCLWW;n+15 zy>a*MRBG&^Pr}Yyx|h<~<|)|DYj;Cvg-smohsCq8Y`AoL>%1t}j62sHB;~zg+yL1* z2C+a4JP?8{eXrfWsn8#NSOsk#T@qt&5K-Ll_#=TVRW$2D_B_E9vWZuK3C|&18S|lE zTlUBs;(}L*_agJ8ezhb(JjgE(!fpakW*QbrTg*C9f^rU{s(|+&hXTE8qX$8vJ!^b$`;}-}ps&9|95s z3KS0e(W3`apxZMb;{Pbmg9eB156Xi*!Ee9HiYf~-O3H~b%S->7Y=2<{^P8na8T7&U z_MhLM9Pe*?$^Rsi6_k?{6ID`Xk`)6-_?7@P$^G90n&`ei--r`Izkd_#{ihV59n()K zTE3_FE}8#N2|;q4KPBAyHR1Od2){9#_!tY_>nCyHzXARZ zT=chuICzdCv`)jZK&7_^m0aW(z_0%U5PU%gTG}}|3p&`FfOK7f`aeXA!5O!{sM{5R znC3wrvR@b-L3#K?5hVXE!(aPLaJ+d5Q95XlxlA6^65mdV|9*13bwQ@&Kj48xXU!e# zK=-Z0faZpR`uc`;cCr9VfbriU3BU>T%e|X)K~;nTB4PX+9rT^!T?eB41A&??z#4RI z4`i({0vbDj^qWCK=6}w+f;%B_15|2UP&Z)t1sd@9kI>+iE&+n8$d3#01@Mvj>=){~2wuLnf#P zRQ@2G=6EwR z{}K9otBE=p>O1^LaT^>JMf%m3JrF8AC^`rzhyQtUycwI0)u0Miy8gE)NuSK% zm&FgF>CE4VetTX2sk?!nT>n6x2W?pXWgY$b92>k4;0K03s7CYtiRypMrQnBPKVTIL z{t5Q`fbkDUV&DY;H+2uJK5$#! z5B!#je}VtsEqmbg1#Y1DK_p-EpM~LnGgrt \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradlew.bat b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradlew.bat new file mode 100644 index 0000000000..8a0b282aa6 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/mappings/.gitkeep b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/mappings/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/Application.java b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/Application.java new file mode 100644 index 0000000000..5a1a60244e --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/Application.java @@ -0,0 +1,17 @@ +package com.blogspot.toomuchcoding.frauddetection; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +@Configuration +@EnableAutoConfiguration +@ComponentScan +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/LoanApplicationService.java b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/LoanApplicationService.java new file mode 100644 index 0000000000..82476a3a46 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/LoanApplicationService.java @@ -0,0 +1,62 @@ +package com.blogspot.toomuchcoding.frauddetection; + +import com.blogspot.toomuchcoding.frauddetection.model.FraudCheckStatus; +import com.blogspot.toomuchcoding.frauddetection.model.FraudServiceRequest; +import com.blogspot.toomuchcoding.frauddetection.model.FraudServiceResponse; +import com.blogspot.toomuchcoding.frauddetection.model.LoanApplication; +import com.blogspot.toomuchcoding.frauddetection.model.LoanApplicationResult; +import com.blogspot.toomuchcoding.frauddetection.model.LoanApplicationStatus; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +@Service +public class LoanApplicationService { + + private static final String FRAUD_SERVICE_JSON_VERSION_1 = + "application/vnd.fraud.v1+json"; + + private final RestTemplate restTemplate; + + public LoanApplicationService() { + this.restTemplate = new RestTemplate(); + } + + public LoanApplicationResult loanApplication(LoanApplication loanApplication) { + FraudServiceRequest request = + new FraudServiceRequest(loanApplication); + + FraudServiceResponse response = + sendRequestToFraudDetectionService(request); + + return buildResponseFromFraudResult(response); + } + + private FraudServiceResponse sendRequestToFraudDetectionService( + FraudServiceRequest request) { + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.add(HttpHeaders.CONTENT_TYPE, FRAUD_SERVICE_JSON_VERSION_1); + + ResponseEntity response = + restTemplate.exchange("http://localhost:8080/fraudcheck", HttpMethod.PUT, + new HttpEntity<>(request, httpHeaders), + FraudServiceResponse.class); + + return response.getBody(); + } + + private LoanApplicationResult buildResponseFromFraudResult(FraudServiceResponse response) { + LoanApplicationStatus applicationStatus = null; + if (FraudCheckStatus.OK == response.getFraudCheckStatus()) { + applicationStatus = LoanApplicationStatus.LOAN_APPLIED; + } else if (FraudCheckStatus.FRAUD == response.getFraudCheckStatus()) { + applicationStatus = LoanApplicationStatus.LOAN_APPLICATION_REJECTED; + } + + return new LoanApplicationResult(applicationStatus, response.getRejectionReason()); + } + +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/Client.java b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/Client.java new file mode 100644 index 0000000000..5e91273eda --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/Client.java @@ -0,0 +1,14 @@ +package com.blogspot.toomuchcoding.frauddetection.model; + +public class Client { + + private String pesel; + + public String getPesel() { + return pesel; + } + + public void setPesel(String pesel) { + this.pesel = pesel; + } +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckStatus.java b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckStatus.java new file mode 100644 index 0000000000..b87c365d51 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudCheckStatus.java @@ -0,0 +1,5 @@ +package com.blogspot.toomuchcoding.frauddetection.model; + +public enum FraudCheckStatus { + OK, FRAUD +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudServiceRequest.java b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudServiceRequest.java new file mode 100644 index 0000000000..ac595998bc --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudServiceRequest.java @@ -0,0 +1,34 @@ +package com.blogspot.toomuchcoding.frauddetection.model; + +import java.math.BigDecimal; + +public class FraudServiceRequest { + + private String clientPesel; + + private BigDecimal loanAmount; + + public FraudServiceRequest() { + } + + public FraudServiceRequest(LoanApplication loanApplication) { + this.clientPesel = loanApplication.getClient().getPesel(); + this.loanAmount = loanApplication.getAmount(); + } + + public String getClientPesel() { + return clientPesel; + } + + public void setClientPesel(String clientPesel) { + this.clientPesel = clientPesel; + } + + public BigDecimal getLoanAmount() { + return loanAmount; + } + + public void setLoanAmount(BigDecimal loanAmount) { + this.loanAmount = loanAmount; + } +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudServiceResponse.java b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudServiceResponse.java new file mode 100644 index 0000000000..9f3353ecbf --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/FraudServiceResponse.java @@ -0,0 +1,27 @@ +package com.blogspot.toomuchcoding.frauddetection.model; + +public class FraudServiceResponse { + + private FraudCheckStatus fraudCheckStatus; + + private String rejectionReason; + + public FraudServiceResponse() { + } + + public FraudCheckStatus getFraudCheckStatus() { + return fraudCheckStatus; + } + + public void setFraudCheckStatus(FraudCheckStatus fraudCheckStatus) { + this.fraudCheckStatus = fraudCheckStatus; + } + + public String getRejectionReason() { + return rejectionReason; + } + + public void setRejectionReason(String rejectionReason) { + this.rejectionReason = rejectionReason; + } +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplication.java b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplication.java new file mode 100644 index 0000000000..816087988b --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplication.java @@ -0,0 +1,36 @@ +package com.blogspot.toomuchcoding.frauddetection.model; + +import java.math.BigDecimal; + +public class LoanApplication { + + private Client client; + + private BigDecimal amount; + + private String loanApplicationId; + + public Client getClient() { + return client; + } + + public void setClient(Client client) { + this.client = client; + } + + public BigDecimal getAmount() { + return amount; + } + + public void setAmount(BigDecimal amount) { + this.amount = amount; + } + + public String getLoanApplicationId() { + return loanApplicationId; + } + + public void setLoanApplicationId(String loanApplicationId) { + this.loanApplicationId = loanApplicationId; + } +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplicationResult.java b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplicationResult.java new file mode 100644 index 0000000000..523f4f2ea3 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplicationResult.java @@ -0,0 +1,32 @@ +package com.blogspot.toomuchcoding.frauddetection.model; + +public class LoanApplicationResult { + + private LoanApplicationStatus loanApplicationStatus; + + private String rejectionReason; + + public LoanApplicationResult() { + } + + public LoanApplicationResult(LoanApplicationStatus loanApplicationStatus, String rejectionReason) { + this.loanApplicationStatus = loanApplicationStatus; + this.rejectionReason = rejectionReason; + } + + public LoanApplicationStatus getLoanApplicationStatus() { + return loanApplicationStatus; + } + + public void setLoanApplicationStatus(LoanApplicationStatus loanApplicationStatus) { + this.loanApplicationStatus = loanApplicationStatus; + } + + public String getRejectionReason() { + return rejectionReason; + } + + public void setRejectionReason(String rejectionReason) { + this.rejectionReason = rejectionReason; + } +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplicationStatus.java b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplicationStatus.java new file mode 100644 index 0000000000..7f7f86e0ea --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/java/com/blogspot/toomuchcoding/frauddetection/model/LoanApplicationStatus.java @@ -0,0 +1,5 @@ +package com.blogspot.toomuchcoding.frauddetection.model; + +public enum LoanApplicationStatus { + LOAN_APPLIED, LOAN_APPLICATION_REJECTED +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/resources/application.yml b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/resources/application.yml new file mode 100644 index 0000000000..e86bbd0e0f --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/main/resources/application.yml @@ -0,0 +1 @@ +server.port=8090 \ No newline at end of file diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/test/groovy/com/blogspot/toomuchcoding/LoanApplicationServiceSpec.groovy b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/test/groovy/com/blogspot/toomuchcoding/LoanApplicationServiceSpec.groovy new file mode 100644 index 0000000000..58d17673ce --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/test/groovy/com/blogspot/toomuchcoding/LoanApplicationServiceSpec.groovy @@ -0,0 +1,50 @@ +package com.blogspot.toomuchcoding + +import com.blogspot.toomuchcoding.frauddetection.Application +import com.blogspot.toomuchcoding.frauddetection.LoanApplicationService +import com.blogspot.toomuchcoding.frauddetection.model.Client +import com.blogspot.toomuchcoding.frauddetection.model.LoanApplication +import com.blogspot.toomuchcoding.frauddetection.model.LoanApplicationResult +import com.blogspot.toomuchcoding.frauddetection.model.LoanApplicationStatus +import com.github.tomakehurst.wiremock.junit.WireMockClassRule +import org.junit.ClassRule +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.SpringApplicationContextLoader +import org.springframework.test.context.ContextConfiguration +import spock.lang.Shared +import spock.lang.Specification + +@ContextConfiguration(loader = SpringApplicationContextLoader, classes = Application) +class LoanApplicationServiceSpec extends Specification { + + @ClassRule + @Shared + WireMockClassRule wireMockRule = new WireMockClassRule() + + @Autowired + LoanApplicationService sut + + def 'should successfully apply for loan'() { + given: + LoanApplication application = + new LoanApplication(client: new Client(pesel: '1234567890'), amount: 123.123) + when: + LoanApplicationResult loanApplication = sut.loanApplication(application) + then: + loanApplication.loanApplicationStatus == LoanApplicationStatus.LOAN_APPLIED + loanApplication.rejectionReason == null + } + + def 'should be rejected due to abnormal loan amount'() { + given: + LoanApplication application = + new LoanApplication(client: new Client(pesel: '1234567890'), amount: 99_999) + when: + LoanApplicationResult loanApplication = sut.loanApplication(application) + then: + loanApplication.loanApplicationStatus == LoanApplicationStatus.LOAN_APPLICATION_REJECTED + loanApplication.rejectionReason == 'Amount too high' + } + + +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsFraud.json b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsFraud.json new file mode 100644 index 0000000000..90383f2ce1 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsFraud.json @@ -0,0 +1,23 @@ +{ + "request": { + "method": "PUT", + "headers": { + "Content-Type": { + "equalTo": "application/vnd.fraud.v1+json" + } + }, + "url": "/fraudcheck", + "bodyPatterns": [ + { + "matches": "\\s*\\{\\s*\"clientPesel\"\\s*:\\s*\"?[0-9]{10}\"?\\s*,\\s*\"loanAmount\"\\s*:\\s*\"?99999\"?\\s*\\}\\s*" + } + ] + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/vnd.fraud.v1+json" + }, + "body": "{\"fraudCheckStatus\":\"FRAUD\",\"rejectionReason\":\"Amount too high\"}" + } +} \ No newline at end of file diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.json b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.json new file mode 100644 index 0000000000..2f47855910 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.json @@ -0,0 +1,23 @@ +{ + "request": { + "method": "PUT", + "headers": { + "Content-Type": { + "equalTo": "application/vnd.fraud.v1+json" + } + }, + "url": "/fraudcheck", + "bodyPatterns": [ + { + "matches": "\\s*\\{\\s*\"clientPesel\"\\s*:\\s*\"?[0-9]{10}\"?\\s*,\\s*\"loanAmount\"\\s*:\\s*\"?123.123\"?\\s*\\}\\s*" + } + ] + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/vnd.fraud.v1+json" + }, + "body": "{\"fraudCheckStatus\":\"OK\",\"rejectionReason\":null}" + } +} \ No newline at end of file diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/settings.gradle b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/settings.gradle new file mode 100644 index 0000000000..6a42a6c7ce --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/settings.gradle @@ -0,0 +1,2 @@ +include ':fraudDetectionService' +include ':loanApplicationService' From 179b78c5c38d8921712c8035be1afd2d21f06116 Mon Sep 17 00:00:00 2001 From: Kamil Szymanski Date: Mon, 27 Jul 2015 11:05:43 +0200 Subject: [PATCH 055/119] Allow using single quotes for quoting in stub definitions --- .../wiremock/WireMockToDslConverter.groovy | 8 +++++++- .../wiremock/WireMockToDslConverterSpec.groovy | 16 +++++++--------- build.gradle | 4 ++-- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/WireMockToDslConverter.groovy b/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/WireMockToDslConverter.groovy index 689edb3d9a..2b377c7e3a 100644 --- a/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/WireMockToDslConverter.groovy +++ b/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/WireMockToDslConverter.groovy @@ -1,6 +1,8 @@ package io.codearte.accurest.wiremock + import groovy.io.FileType import groovy.json.JsonOutput +import groovy.json.JsonParserType import groovy.json.JsonSlurper import groovy.xml.XmlUtil import io.codearte.accurest.dsl.GroovyDsl @@ -14,7 +16,7 @@ class WireMockToDslConverter { } private String convertFromWireMockStub(String wireMockStringStub) { - Object wireMockStub = new JsonSlurper().parseText(wireMockStringStub) + Object wireMockStub = parseStubDefinition(wireMockStringStub) def request = wireMockStub.request def response = wireMockStub.response def bodyPatterns = request.bodyPatterns @@ -55,6 +57,10 @@ class WireMockToDslConverter { """ } + private Object parseStubDefinition(String wireMockStringStub) { + new JsonSlurper().setType(JsonParserType.LAX).parseText(wireMockStringStub) + } + private String buildHeader(String method, Object value) { switch (method) { case 'equalTo': diff --git a/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/WireMockToDslConverterSpec.groovy b/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/WireMockToDslConverterSpec.groovy index 9cb1e9813f..cec123a88d 100755 --- a/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/WireMockToDslConverterSpec.groovy +++ b/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/WireMockToDslConverterSpec.groovy @@ -24,7 +24,7 @@ class WireMockToDslConverterSpec extends Specification { }, "response": { "status": 200, - "body": "{ \\"id\\": { \\"value\\": \\"132\\" }, \\"surname\\": \\"Kowalsky\\", \\"name\\": \\"Jan\\", \\"created\\": \\"2014-02-02 12:23:43\\" }", + "body": '{"id": { "value": "132" }, "surname": "Kowalsky", "name": "Jan", "created": "2014-02-02 12:23:43" }', "headers": { "Content-Type": "text/plain" } @@ -74,7 +74,7 @@ class WireMockToDslConverterSpec extends Specification { } - def 'should convert WireMock stub with response body containing simple JSON'() { + def 'should convert WireMock stub with response body containing JSON with escaped double quotes'() { given: String wireMockStub = '''\ { @@ -193,7 +193,7 @@ class WireMockToDslConverterSpec extends Specification { }, "response": { "status": 200, - "body": "[ {\\"a\\":1, \\"c\\":\\"3\\"}, \\"b\\", \\"a\\" ]", + "body": '[ {"a":1, "c":"3"}, "b", "a" ]', "headers": { "Content-Type": "application/json" } @@ -232,7 +232,6 @@ class WireMockToDslConverterSpec extends Specification { }""") == expectedGroovyDsl } - def 'should convert WireMock stub with response body containing a nested list'() { given: String wireMockStub = '''\ @@ -248,7 +247,7 @@ class WireMockToDslConverterSpec extends Specification { }, "response": { "status": 200, - "body":"[{\\"amount\\":1.01,\\"name\\":\\"Name\\",\\"info\\":{\\"title\\":\\"title1\\",\\"payload\\":null},\\"booleanvalue\\":true,\\"user\\":null},{\\"amount\\":2.01,\\"name\\":\\"Name2\\",\\"info\\":{\\"title\\":\\"title2\\",\\"payload\\":null},\\"booleanvalue\\":true,\\"user\\":null}]" + "body": '[{"amount":1.01, "name":"Name", "info":{"title":"title1", "payload":null}, "booleanvalue":true, "user":null}, {"amount":2.01, "name":"Name2", "info":{"title":"title2", "payload":null}, "booleanvalue":true, "user":null}]' } } ''' @@ -306,7 +305,7 @@ class WireMockToDslConverterSpec extends Specification { "method": "POST", "url": "/test", "bodyPatterns": [{ - "equalTo": "{\\"property1\\":\\"abc\\",\\"property2\\":\\"2017-01\\",\\"property3\\":\\"666\\",\\"property4\\":1428566412}" + "equalTo": '{"property1":"abc", "property2":"2017-01", "property3":"666", "property4":1428566412}' }] }, "response": { @@ -386,7 +385,7 @@ class WireMockToDslConverterSpec extends Specification { "url" : "/test", "method" : "POST", "bodyPatterns" : [ { - "equalToJson" : "{\\"pan\\":\\"4855141150107894\\",\\"expirationDate\\":\\"2017-01\\",\\"dcvx\\":\\"178\\"}", + "equalToJson" : '{"pan":"4855141150107894", "expirationDate":"2017-01", "dcvx":"178"}', "jsonCompareMode" : "LENIENT" } ] }, @@ -427,7 +426,7 @@ class WireMockToDslConverterSpec extends Specification { "url" : "/test", "method" : "POST", "bodyPatterns" : [ { - "equalTo" : "{\\"pan\\":\\"4855141150107894\\",\\"expirationDate\\":\\"2017-01\\",\\"dcvx\\":\\"178\\"}" + "equalTo" : '{"pan":"4855141150107894", "expirationDate":"2017-01", "dcvx":"178"}' } ] }, "response" : { @@ -502,5 +501,4 @@ class WireMockToDslConverterSpec extends Specification { void stubMappingIsValidWireMockStub(String mappingDefinition) { StubMapping.buildFrom(mappingDefinition) } - } diff --git a/build.gradle b/build.gradle index 835037077f..8b8d47bb77 100644 --- a/build.gradle +++ b/build.gradle @@ -91,7 +91,7 @@ project(':accurest-core') { compile 'org.apache.commons:commons-lang3:[3.3,)' testCompile 'cglib:cglib-nodep:2.2' testCompile 'org.objenesis:objenesis:2.1' - testCompile 'com.github.tomakehurst:wiremock:1.53' + testCompile 'com.github.tomakehurst:wiremock:1.57' } } @@ -101,7 +101,7 @@ project(':accurest-converters') { compile 'org.apache.commons:commons-lang3:[3.0,)' compile 'commons-io:commons-io:[2.0,)' compile 'dk.brics.automaton:automaton:1.11-8' // needed for Xeger - testCompile 'com.github.tomakehurst:wiremock:1.53' + testCompile 'com.github.tomakehurst:wiremock:1.57' testCompile 'org.hamcrest:hamcrest-all:1.3' } } From 3ad8b5800450ca4b821cc94c2480609e82bd8852 Mon Sep 17 00:00:00 2001 From: "konrad.dobrzynski" Date: Tue, 28 Jul 2015 10:33:17 +0200 Subject: [PATCH 056/119] [#108] Added possibility to validate against single value --- .../codearte/accurest/wiremock/RecursiveFilesConverter.groovy | 2 +- .../codearte/accurest/builder/SpockMethodBodyBuilder.groovy | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/RecursiveFilesConverter.groovy b/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/RecursiveFilesConverter.groovy index 5eb8f81d45..0f74866305 100644 --- a/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/RecursiveFilesConverter.groovy +++ b/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/RecursiveFilesConverter.groovy @@ -33,7 +33,7 @@ class RecursiveFilesConverter { File newGroovyFile = createTargetFileWithProperName(absoluteTargetPath, sourceFile) newGroovyFile.text = convertedContent } catch (Exception e) { - throw new ConversionAccurestException("Unable to convertion of ${sourceFile.name}", e) + throw new ConversionAccurestException("Unable to make convertion of ${sourceFile.name}", e) } } } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy index 7e7bb697bc..1b26c272ab 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy @@ -81,8 +81,10 @@ class SpockMethodBodyBuilder { addLine('def responseBody = new JsonSlurper().parseText(response.body.asString())') if (responseBody instanceof List) { processArrayElements(responseBody, "", blockBuilder) - } else { + } else if (responseBody instanceof Map) { processMapElement(responseBody, blockBuilder, "") + } else { + processBodyElement(blockBuilder, '', responseBody) } } else if (contentType == ContentType.XML) { addLine('def responseBody = new XmlSlurper().parseText(response.body.asString())') From 722bf280166a9ceda88aa28c13d832c3db0bc4d9 Mon Sep 17 00:00:00 2001 From: Jakub Kubrynski Date: Tue, 28 Jul 2015 11:33:18 +0200 Subject: [PATCH 057/119] Release version: 0.7.2 [ci skip] From 3a8baa2212d810bdecaeccef0c1338169bf6bc09 Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Fri, 31 Jul 2015 13:14:36 +0200 Subject: [PATCH 058/119] #106 has changed the logic in test method generation and the fix from #109 will not work anymore. Modified it to make single value validation possible. Added tests. --- .../builder/SpockMethodBodyBuilder.groovy | 3 +++ .../JaxRsClientSpockMethodBuilderSpec.groovy | 22 +++++++++++++++++++ .../MockMvcSpockMethodBuilderSpec.groovy | 22 +++++++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy index 0101a64c9c..f0d29ebefe 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy @@ -98,6 +98,9 @@ abstract class SpockMethodBodyBuilder { } else if (contentType == ContentType.XML) { bb.addLine("def responseBody = new XmlSlurper().parseText($responseAsString)") // TODO xml validation + } else { + bb.addLine("def responseBody = ($responseAsString)") + processBodyElement(bb, "", responseBody) } } diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodBuilderSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodBuilderSpec.groovy index 6b39c26e8d..f7e0b7d777 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodBuilderSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodBuilderSpec.groovy @@ -359,4 +359,26 @@ class JaxRsClientSpockMethodBuilderSpec extends Specification { then: spockTest.contains("entity('', 'application/octet-stream')") } + + def "should generate test for String in response body"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "POST" + url "test" + } + response { + status 200 + body "test" + } + } + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + def spockTest = blockBuilder.toString() + then: + spockTest.contains('def responseBody = (response.body.asString())') + spockTest.contains('responseBody == "test"') + } } diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy index 55a43d2961..57d840710c 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy @@ -306,4 +306,26 @@ class MockMvcSpockMethodBuilderSpec extends Specification { then: spockTest.contains(".body('')") } + + def "should generate test for String in response body"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "POST" + url "test" + } + response { + status 200 + body "test" + } + } + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + def spockTest = blockBuilder.toString() + then: + spockTest.contains('def responseBody = (response.body.asString())') + spockTest.contains('responseBody == "test"') + } } From 9ccc2a2b8a2025b32e39e69504d43c3c48df657c Mon Sep 17 00:00:00 2001 From: Mariusz Smykula Date: Tue, 4 Aug 2015 23:22:39 +0200 Subject: [PATCH 059/119] Support for stub priority --- .../wiremock/WireMockToDslConverter.groovy | 2 + .../WireMockToDslConverterSpec.groovy | 38 +++++++++++++++++++ .../io/codearte/accurest/dsl/GroovyDsl.groovy | 5 +++ .../accurest/dsl/WireMockStubStrategy.groovy | 14 +++++-- .../accurest/dsl/WireMockGroovyDslSpec.groovy | 29 ++++++++++++++ 5 files changed, 85 insertions(+), 3 deletions(-) diff --git a/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/WireMockToDslConverter.groovy b/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/WireMockToDslConverter.groovy index 2b377c7e3a..e7a2a45f97 100644 --- a/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/WireMockToDslConverter.groovy +++ b/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/WireMockToDslConverter.groovy @@ -17,11 +17,13 @@ class WireMockToDslConverter { private String convertFromWireMockStub(String wireMockStringStub) { Object wireMockStub = parseStubDefinition(wireMockStringStub) + Integer priority = wireMockStub.priority def request = wireMockStub.request def response = wireMockStub.response def bodyPatterns = request.bodyPatterns String urlPattern = request.urlPattern return """\ + ${priority ? "priority ${priority}" : ''} request { ${request.method ? "method \"\"\"$request.method\"\"\"" : ""} ${request.url ? "url \"\"\"$request.url\"\"\"" : ""} diff --git a/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/WireMockToDslConverterSpec.groovy b/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/WireMockToDslConverterSpec.groovy index cec123a88d..80e8592276 100755 --- a/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/WireMockToDslConverterSpec.groovy +++ b/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/WireMockToDslConverterSpec.groovy @@ -498,6 +498,44 @@ class WireMockToDslConverterSpec extends Specification { evaluatedGroovyDsl == expectedGroovyDsl } + def 'should convert WireMock stub with priorities'() { + given: + String wireMockStub = '''\ + { + "priority" : 2, + "request" : { + "url" : "/test", + "method" : "POST" + }, + "response" : { + "status" : 200 + } + } + ''' + and: + stubMappingIsValidWireMockStub(wireMockStub) + and: + GroovyDsl expectedGroovyDsl = GroovyDsl.make { + priority 2 + request { + method 'POST' + url '/test' + } + response { + status 200 + } + } + when: + String groovyDsl = WireMockToDslConverter.fromWireMockStub(wireMockStub) + then: + GroovyDsl evaluatedGroovyDsl = new GroovyShell(this.class.classLoader).evaluate( + """ io.codearte.accurest.dsl.GroovyDsl.make { + $groovyDsl + }""") + and: + evaluatedGroovyDsl == expectedGroovyDsl + } + void stubMappingIsValidWireMockStub(String mappingDefinition) { StubMapping.buildFrom(mappingDefinition) } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/GroovyDsl.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/GroovyDsl.groovy index 1007e14cec..93f65f6678 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/GroovyDsl.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/GroovyDsl.groovy @@ -11,6 +11,7 @@ import io.codearte.accurest.dsl.internal.Response @ToString(includeFields = true, includePackage = false, includeNames = true) class GroovyDsl { + Integer priority Request request Response response @@ -21,6 +22,10 @@ class GroovyDsl { return dsl } + void priority(int priority) { + this.priority = new Integer(priority) + } + void request(@DelegatesTo(Request) Closure closure) { this.request = new Request() closure.delegate = request diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockStubStrategy.groovy index 1793b8e61c..acff79a11a 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockStubStrategy.groovy @@ -1,6 +1,7 @@ package io.codearte.accurest.dsl import groovy.json.JsonOutput +import groovy.transform.CompileDynamic import groovy.transform.CompileStatic @CompileStatic @@ -8,14 +9,21 @@ class WireMockStubStrategy { private final WireMockRequestStubStrategy wireMockRequestStubStrategy private final WireMockResponseStubStrategy wireMockResponseStubStrategy + private final Integer priority WireMockStubStrategy(GroovyDsl groovyDsl) { this.wireMockRequestStubStrategy = new WireMockRequestStubStrategy(groovyDsl) this.wireMockResponseStubStrategy = new WireMockResponseStubStrategy(groovyDsl) + this.priority = groovyDsl.priority } + @CompileDynamic String toWireMockClientStub() { - return JsonOutput.prettyPrint(JsonOutput.toJson([request : wireMockRequestStubStrategy.buildClientRequestContent(), - response: wireMockResponseStubStrategy.buildClientResponseContent()])) + def wiremockStubDefinition = [request : wireMockRequestStubStrategy.buildClientRequestContent(), + response: wireMockResponseStubStrategy.buildClientResponseContent()] + if (priority) { + wiremockStubDefinition.priority = priority + } + return JsonOutput.prettyPrint(JsonOutput.toJson(wiremockStubDefinition)) } -} +} \ No newline at end of file diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy index 12aca1f271..ced69beeb1 100755 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy @@ -1152,6 +1152,35 @@ class WireMockGroovyDslSpec extends WireMockSpec { ''') } + def "should generate stub with priority"() { + given: + GroovyDsl groovyDsl = GroovyDsl.make { + priority 9 + request { + method('POST') + url("test") + } + response { + status 406 + } + } + when: + def json = toWireMockClientJsonStub(groovyDsl) + then: + parseJson(json) == parseJson(''' + { + "priority": 9, + "request": { + "method": "POST", + "url": "test" + }, + "response": { + "status": 406 + } + } + ''') + } + String toJsonString(value) { new JsonBuilder(value).toPrettyString() } From e3733dd6c32b1d26bb3d03f8fcb7aab00f1c82a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mariusz=20Smyku=C5=82a?= Date: Wed, 5 Aug 2015 11:08:41 +0200 Subject: [PATCH 060/119] small code improvment --- .../src/main/groovy/io/codearte/accurest/dsl/GroovyDsl.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/GroovyDsl.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/GroovyDsl.groovy index 93f65f6678..af046d188f 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/GroovyDsl.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/GroovyDsl.groovy @@ -23,7 +23,7 @@ class GroovyDsl { } void priority(int priority) { - this.priority = new Integer(priority) + this.priority = priority } void request(@DelegatesTo(Request) Closure closure) { From 0109ce42338ca085db04116c890648e29dae7d52 Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Wed, 5 Aug 2015 16:19:03 +0200 Subject: [PATCH 061/119] Added priority to the sample project DSL and test. Changes in tests to reflect stub-priority feature added by @mariuszs. --- .../io/codearte/accurest/plugin/BasicFunctionalSpec.groovy | 1 + .../pairId/colleratePlacesFromTweet.groovy | 1 + 2 files changed, 2 insertions(+) diff --git a/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/BasicFunctionalSpec.groovy b/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/BasicFunctionalSpec.groovy index d488f03ff4..ce2d32dafe 100755 --- a/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/BasicFunctionalSpec.groovy +++ b/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/BasicFunctionalSpec.groovy @@ -43,6 +43,7 @@ class BasicFunctionalSpec extends IntegrationSpec { def generatedClientJsonStub = file(GENERATED_CLIENT_JSON_STUB).text new JsonSlurper().parseText(generatedClientJsonStub) == new JsonSlurper().parseText(""" { + "priority: 2", "request": { "method": "PUT", "headers": { diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/repository/mappings/com/ofg/twitter-places-analyzer/pairId/colleratePlacesFromTweet.groovy b/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/repository/mappings/com/ofg/twitter-places-analyzer/pairId/colleratePlacesFromTweet.groovy index 08b137a054..6fe36c6a89 100644 --- a/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/repository/mappings/com/ofg/twitter-places-analyzer/pairId/colleratePlacesFromTweet.groovy +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/repository/mappings/com/ofg/twitter-places-analyzer/pairId/colleratePlacesFromTweet.groovy @@ -1,4 +1,5 @@ io.codearte.accurest.dsl.GroovyDsl.make { + priority 2 request { method 'PUT' url '/api/12' From daa3ccb7ea0a55f6af16c17aeac702f8d73a7222 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Wed, 5 Aug 2015 18:01:03 +0200 Subject: [PATCH 062/119] [#113] Fixed missing regex in headers --- .../MockMvcSpockMethodBodyBuilder.groovy | 12 ++++++- .../MockMvcSpockMethodBuilderSpec.groovy | 33 +++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBodyBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBodyBuilder.groovy index 40d6a24416..d686bcc54e 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBodyBuilder.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBodyBuilder.groovy @@ -9,6 +9,8 @@ import io.codearte.accurest.dsl.internal.QueryParameter import io.codearte.accurest.dsl.internal.Request import io.codearte.accurest.dsl.internal.UrlPath +import java.util.regex.Pattern + @PackageScope @TypeChecked class MockMvcSpockMethodBodyBuilder extends SpockMethodBodyBuilder { @@ -46,10 +48,18 @@ class MockMvcSpockMethodBodyBuilder extends SpockMethodBodyBuilder { protected void validateResponseHeadersBlock(BlockBuilder bb) { response.headers?.collect { Header header -> - bb.addLine("response.header('$header.name') == '$header.serverValue'") + bb.addLine("response.header('$header.name') ${convertHeaderComparison(header.serverValue)}") } } + private String convertHeaderComparison(Object headerValue) { + return " == '$headerValue'" + } + + private String convertHeaderComparison(Pattern headerValue) { + return "==~ java.util.regex.Pattern.compile('$headerValue')" + } + @Override protected String getResponseAsString() { return 'response.body.asString()' diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy index 57d840710c..d29c1f87b5 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy @@ -328,4 +328,37 @@ class MockMvcSpockMethodBuilderSpec extends Specification { spockTest.contains('def responseBody = (response.body.asString())') spockTest.contains('responseBody == "test"') } + + @Issue('113') + def "should generate regex test for String in response header"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method 'POST' + url $(client(regex('/partners/[0-9]+/users')), server('/partners/1000/users')) + headers { header 'Content-Type': 'application/json' } + body( + first_name: 'John', + last_name: 'Smith', + personal_id: '12345678901', + phone_number: '500500500', + invitation_token: '00fec7141bb94793bfe7ae1d0f39bda0', + password: 'john' + ) + } + response { + status 201 + headers { + header 'Location': $(client('http://localhost/partners/1000/users/1001'), server(regex('http://localhost/partners/[0-9]+/users/[0-9]+'))) + } + } + } + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + def spockTest = blockBuilder.toString() + then: + spockTest.contains('''response.header('Location') ==~ java.util.regex.Pattern.compile('http://localhost/partners/[0-9]+/users/[0-9]+')''') + } } From 96aae0895c0b84d5dca9a25af04390db0a6ba6c7 Mon Sep 17 00:00:00 2001 From: Mariusz Smykula Date: Fri, 7 Aug 2015 14:33:48 +0200 Subject: [PATCH 063/119] test fix, bad sample json --- .../io/codearte/accurest/plugin/BasicFunctionalSpec.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/BasicFunctionalSpec.groovy b/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/BasicFunctionalSpec.groovy index ce2d32dafe..5b101daa03 100755 --- a/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/BasicFunctionalSpec.groovy +++ b/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/BasicFunctionalSpec.groovy @@ -43,7 +43,7 @@ class BasicFunctionalSpec extends IntegrationSpec { def generatedClientJsonStub = file(GENERATED_CLIENT_JSON_STUB).text new JsonSlurper().parseText(generatedClientJsonStub) == new JsonSlurper().parseText(""" { - "priority: 2", + "priority": 2, "request": { "method": "PUT", "headers": { From d940265111de53438bfba00e4433c7c6b51da6bb Mon Sep 17 00:00:00 2001 From: Mariusz Smykula Date: Fri, 7 Aug 2015 14:35:07 +0200 Subject: [PATCH 064/119] Release version: 0.8.1 [ci skip] From b51dd53b7bc8350ca136d9e01e2e5a1c56e5c24a Mon Sep 17 00:00:00 2001 From: Mariusz Smykula Date: Fri, 7 Aug 2015 14:36:01 +0200 Subject: [PATCH 065/119] Release version: 0.8.1 [ci skip] From e0436ace9fd62744b12b17994dc51eb9e455537d Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Fri, 7 Aug 2015 21:38:54 +0200 Subject: [PATCH 066/119] [#115] added possibility to have some predefined regex --- .../accurest/dsl/internal/Common.groovy | 10 ++-- .../dsl/internal/RegexPatterns.groovy | 30 +++++++++++ .../MockMvcSpockMethodBuilderSpec.groovy | 33 ++++++++++++ .../dsl/internal/RegexPatternsSpec.groovy | 53 +++++++++++++++++++ 4 files changed, 122 insertions(+), 4 deletions(-) create mode 100644 accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/RegexPatterns.groovy create mode 100644 accurest-core/src/test/groovy/io/codearte/accurest/dsl/internal/RegexPatternsSpec.groovy diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Common.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Common.groovy index 87d509de59..9030d9b9f3 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Common.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Common.groovy @@ -13,6 +13,8 @@ import java.util.regex.Pattern @PackageScope class Common { + @Delegate private final RegexPatterns regexPatterns = new RegexPatterns() + Map convertObjectsToDslProperties(Map body) { return body.collectEntries { Map.Entry entry -> @@ -80,12 +82,12 @@ class Common { return new ServerDslProperty(serverValue) } - void assertThatSidesMatch(Pattern firstSide, String secondSide) { - assert secondSide ==~ firstSide + void assertThatSidesMatch(Pattern pattern, String value) { + assert value ==~ pattern } - void assertThatSidesMatch(String firstSide, Pattern secondSide) { - assert firstSide ==~ secondSide + void assertThatSidesMatch(String value, Pattern pattern) { + assert value ==~ pattern } void assertThatSidesMatch(MatchingStrategy firstSide, MatchingStrategy secondSide) { diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/RegexPatterns.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/RegexPatterns.groovy new file mode 100644 index 0000000000..210668a2a9 --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/RegexPatterns.groovy @@ -0,0 +1,30 @@ +package io.codearte.accurest.dsl.internal + +import groovy.transform.CompileStatic + +import java.util.regex.Pattern + +@CompileStatic +class RegexPatterns { + + private 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])'); + private static final Pattern HOSTNAME_PATTERN = Pattern.compile('((http[s]?|ftp):\\/)\\/?([^:\\/\\s]+)(:[0-9]{1,5})?'); + private static final Pattern EMAIL = Pattern.compile('[_]*([a-z0-9]+(\\.|_*)?)+@([a-z][a-z0-9-]+(\\.|-*\\.))+[a-z]{2,6}'); + private static final Pattern URL = Pattern.compile('((www\\.|(http|https|ftp|news|file)+\\:\\/\\/)[_.a-z0-9-]+\\.[a-z0-9\\/_:@=.+?,##%&~-]*[^.|\\\'|\\# |!|\\(|?|,| |>|<|;|\\)])'); + + String ipAddress() { + return IP_ADDRESS.pattern() + } + + String hostname() { + return HOSTNAME_PATTERN.pattern() + } + + String email() { + return EMAIL.pattern() + } + + String url() { + return URL.pattern() + } +} diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy index d29c1f87b5..6d2ed106d1 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy @@ -361,4 +361,37 @@ class MockMvcSpockMethodBuilderSpec extends Specification { then: spockTest.contains('''response.header('Location') ==~ java.util.regex.Pattern.compile('http://localhost/partners/[0-9]+/users/[0-9]+')''') } + + @Issue('115') + def "should generate regex with helper method"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method 'POST' + url $(client(regex('/partners/[0-9]+/users')), server('/partners/1000/users')) + headers { header 'Content-Type': 'application/json' } + body( + first_name: 'John', + last_name: 'Smith', + personal_id: '12345678901', + phone_number: '500500500', + invitation_token: '00fec7141bb94793bfe7ae1d0f39bda0', + password: 'john' + ) + } + response { + status 201 + headers { + header 'Location': $(client('http://localhost/partners/1000/users/1001'), server(regex("^${hostname()}/partners/[0-9]+/users/[0-9]+"))) + } + } + } + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + def spockTest = blockBuilder.toString() + then: + spockTest.contains('''response.header('Location') ==~ java.util.regex.Pattern.compile('^((http[s]?|ftp):\\/)\\/?([^:\\/\\s]+)(:[0-9]{1,5})?/partners/[0-9]+/users/[0-9]+')''') + } } diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/internal/RegexPatternsSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/internal/RegexPatternsSpec.groovy new file mode 100644 index 0000000000..38430aa42f --- /dev/null +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/internal/RegexPatternsSpec.groovy @@ -0,0 +1,53 @@ +package io.codearte.accurest.dsl.internal + +import spock.lang.Specification +import spock.lang.Unroll + +import java.util.regex.Pattern + +class RegexPatternsSpec extends Specification { + + RegexPatterns regexPatterns = new RegexPatterns() + + @Unroll + def "should generate a regex for ip address [#textToMatch] that is a match [#shouldMatch]"() { + expect: + shouldMatch == Pattern.compile(regexPatterns.ipAddress()).matcher(textToMatch).matches() + where: + textToMatch || shouldMatch + '123.123.123.123' || true + 'a.b.' || false + } + + @Unroll + def "should generate a regex for hostname [#textToMatch] that is a match [#shouldMatch]"() { + expect: + shouldMatch == Pattern.compile(regexPatterns.hostname()).matcher(textToMatch).matches() + where: + textToMatch || shouldMatch + 'https://asd.com' || true + 'https://asd.com:8080' || true + 'https://asd.com/asd' || false + 'asd.com' || false + } + + @Unroll + def "should generate a regex for email [#textToMatch] that is a match [#shouldMatch]"() { + expect: + shouldMatch == Pattern.compile(regexPatterns.email()).matcher(textToMatch).matches() + where: + textToMatch || shouldMatch + 'asd@asd.com' || true + 'a.b.' || false + } + + @Unroll + 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 + } +} From f3efc6b9dd3deb72071146643bd6bd7017304fbe Mon Sep 17 00:00:00 2001 From: Adam Wojszczyk Date: Mon, 10 Aug 2015 11:49:35 +0200 Subject: [PATCH 067/119] Add localhost assertions to regexPatternsSpec --- .../accurest/dsl/internal/RegexPatternsSpec.groovy | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/internal/RegexPatternsSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/internal/RegexPatternsSpec.groovy index 38430aa42f..848ed8511d 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/internal/RegexPatternsSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/internal/RegexPatternsSpec.groovy @@ -24,11 +24,13 @@ class RegexPatternsSpec extends Specification { expect: shouldMatch == Pattern.compile(regexPatterns.hostname()).matcher(textToMatch).matches() where: - textToMatch || shouldMatch - 'https://asd.com' || true - 'https://asd.com:8080' || true - 'https://asd.com/asd' || false - 'asd.com' || false + textToMatch || shouldMatch + 'https://asd.com' || true + 'https://asd.com:8080' || true + 'https://localhost' || true + 'https://localhost:8080' || true + 'https://asd.com/asd' || false + 'asd.com' || false } @Unroll From 5bc9fe938704f5b22cbdc8b0a01819b09a225223 Mon Sep 17 00:00:00 2001 From: Spencer Gibb Date: Mon, 10 Aug 2015 10:37:44 -0600 Subject: [PATCH 068/119] Some grammar and formatting changes --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6bf509470d..b079b674a9 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,10 @@ Accurate REST Consumer Driven Contracts verifier for Java -Just to make long story short - AccuREST is a tool for Consumer Driven Contract (CDC) development. AccuREST ships easy DSL for describing REST contracts for JVM-based applications. The contract DSL is used by AccuREST for two things: +To make a long story short - AccuREST is a tool for Consumer Driven Contract (CDC) development. AccuREST ships an easy DSL for describing REST contracts for JVM-based applications. The contract DSL is used by AccuREST for two things: -generating WireMock's JSON stub definitions, allowing rapid development of the consumer side, +1. generating WireMock's JSON stub definitions, allowing rapid development of the consumer side, generating Spock's acceptance tests for the server - to verify if your API implementation is compliant with the contract. -By using AccuREST you can move TDD to an architecture level. +2. moving TDD to an architecture level. -For more information please follow to the [Wiki](https://github.com/Codearte/accurest/wiki/1.-Introduction) +For more information please go to the [Wiki](https://github.com/Codearte/accurest/wiki/1.-Introduction) From 7d39ef49b46eac2878c14a416d38b10b292905ef Mon Sep 17 00:00:00 2001 From: Adam Wojszczyk Date: Mon, 17 Aug 2015 15:48:06 +0200 Subject: [PATCH 069/119] Allow fields' values to be empty lists during searching for patterns --- .../dsl/WireMockRequestStubStrategy.groovy | 2 +- .../accurest/dsl/WireMockGroovyDslSpec.groovy | 35 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockRequestStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockRequestStubStrategy.groovy index d0bb325bb1..d540069484 100755 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockRequestStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockRequestStubStrategy.groovy @@ -139,7 +139,7 @@ class WireMockRequestStubStrategy extends BaseWireMockStubStrategy { } private boolean containsPattern(Collection collection) { - return collection.collect(this.&containsPattern).inject { a, b -> a || b } + return collection.collect(this.&containsPattern).inject('') { a, b -> a || b } } private boolean containsPattern(Object[] objects) { diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy index ced69beeb1..23d8f65f35 100755 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy @@ -1192,4 +1192,39 @@ class WireMockGroovyDslSpec extends WireMockSpec { String toWireMockClientJsonStub(groovyDsl) { new WireMockStubStrategy(groovyDsl).toWireMockClientStub() } + + def 'should generate stub with empty list as a value of a field'() { + given: + GroovyDsl groovyDsl = GroovyDsl.make { + request { + method('POST') + body( + values: [] + ) + } + response { + status 200 + } + } + when: + String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() + then: + new JsonSlurper().parseText(wireMockStub) == new JsonSlurper().parseText(''' + { + "request": { + "method": "POST", + "bodyPatterns": [ + { + "equalToJson": "{\\"values\\":[]}" + } + ] + }, + "response": { + "status": 200 + } + } + ''') + and: + stubMappingIsValidWireMockStub(wireMockStub) + } } From b3b9dced26c7de1ee228a0613256164a46a7c611 Mon Sep 17 00:00:00 2001 From: Adam Wojszczyk Date: Mon, 17 Aug 2015 16:03:33 +0200 Subject: [PATCH 070/119] [#121] Allow fields' values to be empty lists during searching for patterns --- .../groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy index 23d8f65f35..d38124d532 100755 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy @@ -1193,6 +1193,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { new WireMockStubStrategy(groovyDsl).toWireMockClientStub() } + @Issue("#121") def 'should generate stub with empty list as a value of a field'() { given: GroovyDsl groovyDsl = GroovyDsl.make { From ee87decf82a43adba091149a9ea6ace0df5a3eef Mon Sep 17 00:00:00 2001 From: Adam Wojszczyk Date: Mon, 17 Aug 2015 16:05:32 +0200 Subject: [PATCH 071/119] [#121] Allow fields' values to be empty lists during searching for patterns - reformat --- .../accurest/dsl/WireMockGroovyDslSpec.groovy | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy index d38124d532..1867c49eda 100755 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy @@ -1181,18 +1181,6 @@ class WireMockGroovyDslSpec extends WireMockSpec { ''') } - String toJsonString(value) { - new JsonBuilder(value).toPrettyString() - } - - Object parseJson(json) { - new JsonSlurper().parseText(json) - } - - String toWireMockClientJsonStub(groovyDsl) { - new WireMockStubStrategy(groovyDsl).toWireMockClientStub() - } - @Issue("#121") def 'should generate stub with empty list as a value of a field'() { given: @@ -1200,7 +1188,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { request { method('POST') body( - values: [] + values: [] ) } response { @@ -1228,4 +1216,16 @@ class WireMockGroovyDslSpec extends WireMockSpec { and: stubMappingIsValidWireMockStub(wireMockStub) } + + String toJsonString(value) { + new JsonBuilder(value).toPrettyString() + } + + Object parseJson(json) { + new JsonSlurper().parseText(json) + } + + String toWireMockClientJsonStub(groovyDsl) { + new WireMockStubStrategy(groovyDsl).toWireMockClientStub() + } } From 7879779e48ca8651f40abd635825c779eeec40e9 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Sat, 22 Aug 2015 09:37:35 +0200 Subject: [PATCH 072/119] [#124] Multiline strings support. Fixes #124 --- .../builder/SpockMethodBodyBuilder.groovy | 2 +- .../JaxRsClientSpockMethodBuilderSpec.groovy | 35 ++++--- .../MockMvcSpockMethodBuilderSpec.groovy | 93 ++++++++++++------- 3 files changed, 80 insertions(+), 50 deletions(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy index f0d29ebefe..af8993e140 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy @@ -155,7 +155,7 @@ abstract class SpockMethodBodyBuilder { value = value.substring(1).replaceAll('\\$value', "responseBody$property") blockBuilder.addLine(value) } else { - blockBuilder.addLine("responseBody$property == \"${value}\"") + blockBuilder.addLine("responseBody$property == '''${value}'''") } } diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodBuilderSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodBuilderSpec.groovy index f7e0b7d777..07123d6265 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodBuilderSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodBuilderSpec.groovy @@ -26,8 +26,8 @@ class JaxRsClientSpockMethodBuilderSpec extends Specification { when: builder.appendTo(blockBuilder) then: - blockBuilder.toString().contains("responseBody.property1 == \"a\"") - blockBuilder.toString().contains("responseBody.property2 == \"b\"") + blockBuilder.toString().contains("responseBody.property1 == '''a'''") + blockBuilder.toString().contains("responseBody.property2 == '''b'''") } @Issue("#79") @@ -54,9 +54,9 @@ class JaxRsClientSpockMethodBuilderSpec extends Specification { when: builder.appendTo(blockBuilder) then: - blockBuilder.toString().contains("responseBody.property1 == \"a\"") - blockBuilder.toString().contains("responseBody.property2[0].a == \"sth\"") - blockBuilder.toString().contains("responseBody.property2[1].b == \"sthElse\"") + blockBuilder.toString().contains("responseBody.property1 == '''a'''") + blockBuilder.toString().contains("responseBody.property2[0].a == '''sth'''") + blockBuilder.toString().contains("responseBody.property2[1].b == '''sthElse'''") } @Issue("#82") @@ -128,8 +128,8 @@ class JaxRsClientSpockMethodBuilderSpec extends Specification { when: builder.appendTo(blockBuilder) then: - blockBuilder.toString().contains("responseBody[0].property1 == \"a\"") - blockBuilder.toString().contains("responseBody[1].property2 == \"b\"") + blockBuilder.toString().contains("responseBody[0].property1 == '''a'''") + blockBuilder.toString().contains("responseBody[1].property2 == '''b'''") } def "should generate assertions for array inside response body element"() { @@ -154,8 +154,8 @@ class JaxRsClientSpockMethodBuilderSpec extends Specification { when: builder.appendTo(blockBuilder) then: - blockBuilder.toString().contains("responseBody.property1[0].property2 == \"test1\"") - blockBuilder.toString().contains("responseBody.property1[1].property3 == \"test2\"") + blockBuilder.toString().contains("responseBody.property1[0].property2 == '''test1'''") + blockBuilder.toString().contains("responseBody.property1[1].property3 == '''test2'''") } def "should generate assertions for nested objects in response body"() { @@ -180,8 +180,8 @@ class JaxRsClientSpockMethodBuilderSpec extends Specification { when: builder.appendTo(blockBuilder) then: - blockBuilder.toString().contains("responseBody.property1 == \"a\"") - blockBuilder.toString().contains("responseBody.property2.property3 == \"b\"") + blockBuilder.toString().contains("responseBody.property1 == '''a'''") + blockBuilder.toString().contains("responseBody.property2.property3 == '''b'''") } def "should generate regex assertions for map objects in response body"() { @@ -212,7 +212,7 @@ class JaxRsClientSpockMethodBuilderSpec extends Specification { when: builder.appendTo(blockBuilder) then: - blockBuilder.toString().contains("responseBody.property1 == \"a\"") + blockBuilder.toString().contains("responseBody.property1 == '''a'''") blockBuilder.toString().contains("responseBody.property2 ==~ java.util.regex.Pattern.compile('[0-9]{3}')") } @@ -238,7 +238,7 @@ class JaxRsClientSpockMethodBuilderSpec extends Specification { when: builder.appendTo(blockBuilder) then: - blockBuilder.toString().contains("responseBody.property1 == \"a\"") + blockBuilder.toString().contains("responseBody.property1 == '''a'''") blockBuilder.toString().contains("responseBody.property2 ==~ java.util.regex.Pattern.compile('[0-9]{3}')") } @@ -335,8 +335,8 @@ class JaxRsClientSpockMethodBuilderSpec extends Specification { spockTest.contains("queryParam('age', '99'") spockTest.contains("queryParam('name', 'Denis.Stepanov'") spockTest.contains("queryParam('email', 'bob@email.com'") - spockTest.contains('responseBody.property1 == "a"') - spockTest.contains('responseBody.property2 == "b"') + spockTest.contains("responseBody.property1 == '''a'''") + spockTest.contains("responseBody.property2 == '''b'''") } def "should generate test for empty body"() { @@ -372,13 +372,12 @@ class JaxRsClientSpockMethodBuilderSpec extends Specification { body "test" } } - MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) def spockTest = blockBuilder.toString() then: - spockTest.contains('def responseBody = (response.body.asString())') - spockTest.contains('responseBody == "test"') + spockTest.contains("responseBody == '''test'''") } } diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy index 6d2ed106d1..c314de48ca 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy @@ -3,13 +3,15 @@ package io.codearte.accurest.builder import io.codearte.accurest.dsl.GroovyDsl import spock.lang.Issue import spock.lang.Specification +import spock.lang.Unroll /** * @author Jakub Kubrynski */ class MockMvcSpockMethodBuilderSpec extends Specification { - def "should generate assertions for simple response body"() { + @Unroll + def "should generate assertions for simple response body for [#spockMethodBuilder]"() { given: GroovyDsl contractDsl = GroovyDsl.make { request { @@ -24,13 +26,13 @@ class MockMvcSpockMethodBuilderSpec extends Specification { }""" } } - MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + SpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) then: - blockBuilder.toString().contains("responseBody.property1 == \"a\"") - blockBuilder.toString().contains("responseBody.property2 == \"b\"") + blockBuilder.toString().contains("responseBody.property1 == '''a'''") + blockBuilder.toString().contains("responseBody.property2 == '''b'''") } @Issue("#79") @@ -52,14 +54,14 @@ class MockMvcSpockMethodBuilderSpec extends Specification { ) } } - MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + SpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) then: - blockBuilder.toString().contains("responseBody.property1 == \"a\"") - blockBuilder.toString().contains("responseBody.property2[0].a == \"sth\"") - blockBuilder.toString().contains("responseBody.property2[1].b == \"sthElse\"") + blockBuilder.toString().contains("responseBody.property1 == '''a'''") + blockBuilder.toString().contains("responseBody.property2[0].a == '''sth'''") + blockBuilder.toString().contains("responseBody.property2[1].b == '''sthElse'''") } @Issue("#82") @@ -77,7 +79,7 @@ class MockMvcSpockMethodBuilderSpec extends Specification { status 200 } } - MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + SpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) @@ -100,7 +102,7 @@ class MockMvcSpockMethodBuilderSpec extends Specification { status 200 } } - MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + SpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) @@ -126,13 +128,13 @@ class MockMvcSpockMethodBuilderSpec extends Specification { }]""" } } - MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + SpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) then: - blockBuilder.toString().contains("responseBody[0].property1 == \"a\"") - blockBuilder.toString().contains("responseBody[1].property2 == \"b\"") + blockBuilder.toString().contains("responseBody[0].property1 == '''a'''") + blockBuilder.toString().contains("responseBody[1].property2 == '''b'''") } def "should generate assertions for array inside response body element"() { @@ -152,13 +154,13 @@ class MockMvcSpockMethodBuilderSpec extends Specification { }""" } } - MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + SpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) then: - blockBuilder.toString().contains("responseBody.property1[0].property2 == \"test1\"") - blockBuilder.toString().contains("responseBody.property1[1].property3 == \"test2\"") + blockBuilder.toString().contains("responseBody.property1[0].property2 == '''test1'''") + blockBuilder.toString().contains("responseBody.property1[1].property3 == '''test2'''") } def "should generate assertions for nested objects in response body"() { @@ -178,13 +180,13 @@ class MockMvcSpockMethodBuilderSpec extends Specification { ''' } } - MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + SpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) then: - blockBuilder.toString().contains("responseBody.property1 == \"a\"") - blockBuilder.toString().contains("responseBody.property2.property3 == \"b\"") + blockBuilder.toString().contains("responseBody.property1 == '''a'''") + blockBuilder.toString().contains("responseBody.property2.property3 == '''b'''") } def "should generate regex assertions for map objects in response body"() { @@ -210,12 +212,12 @@ class MockMvcSpockMethodBuilderSpec extends Specification { } } - MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + SpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) then: - blockBuilder.toString().contains("responseBody.property1 == \"a\"") + blockBuilder.toString().contains("responseBody.property1 == '''a'''") blockBuilder.toString().contains("responseBody.property2 ==~ java.util.regex.Pattern.compile('[0-9]{3}')") } @@ -236,13 +238,14 @@ class MockMvcSpockMethodBuilderSpec extends Specification { } } - MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + SpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) then: - blockBuilder.toString().contains("responseBody.property1 == \"a\"") + blockBuilder.toString().contains("responseBody.property1 == '''a'''") blockBuilder.toString().contains("responseBody.property2 ==~ java.util.regex.Pattern.compile('[0-9]{3}')") + } def "should generate a call with an url path and query parameters"() { @@ -275,15 +278,15 @@ class MockMvcSpockMethodBuilderSpec extends Specification { """ } } - MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + SpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) def spockTest = blockBuilder.toString() then: spockTest.contains('get("/users?limit=10&offset=20&filter=email&sort=name&search=55&age=99&name=Denis.Stepanov&email=bob@email.com")') - spockTest.contains('responseBody.property1 == "a"') - spockTest.contains('responseBody.property2 == "b"') + spockTest.contains("responseBody.property1 == '''a'''") + spockTest.contains("responseBody.property2 == '''b'''") } def "should generate test for empty body"() { @@ -298,7 +301,7 @@ class MockMvcSpockMethodBuilderSpec extends Specification { status 406 } } - MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + SpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) @@ -319,14 +322,14 @@ class MockMvcSpockMethodBuilderSpec extends Specification { body "test" } } - MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + SpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) def spockTest = blockBuilder.toString() then: spockTest.contains('def responseBody = (response.body.asString())') - spockTest.contains('responseBody == "test"') + spockTest.contains("responseBody == '''test'''") } @Issue('113') @@ -353,7 +356,7 @@ class MockMvcSpockMethodBuilderSpec extends Specification { } } } - MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + SpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) @@ -386,7 +389,7 @@ class MockMvcSpockMethodBuilderSpec extends Specification { } } } - MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + SpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) @@ -394,4 +397,32 @@ class MockMvcSpockMethodBuilderSpec extends Specification { then: spockTest.contains('''response.header('Location') ==~ java.util.regex.Pattern.compile('^((http[s]?|ftp):\\/)\\/?([^:\\/\\s]+)(:[0-9]{1,5})?/partners/[0-9]+/users/[0-9]+')''') } + + @Issue('124') + def "should create proper response body matching for multiline strings"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + + request { + method 'POST' + url '/invitations' + } + + response { + status 422 + body( + message: "First line\n" + + " Second line" + ) + } + } + SpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + def spockTest = blockBuilder.toString() + then: + spockTest.contains("""responseBody.message == '''First line + Second line'''""") + } } From 23446397ba8e2e292a6b24500141e5310b1ac899 Mon Sep 17 00:00:00 2001 From: Adam Wojszczyk Date: Mon, 24 Aug 2015 16:13:04 +0200 Subject: [PATCH 073/119] [#127] Add stub as an alias for client and test as an alias for server --- .../accurest/dsl/internal/Common.groovy | 8 +++++ .../MockMvcSpockMethodBuilderSpec.groovy | 31 ++++++++++++++++ .../accurest/dsl/WireMockGroovyDslSpec.groovy | 36 +++++++++++++++++++ 3 files changed, 75 insertions(+) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Common.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Common.groovy index 9030d9b9f3..6cb78b67ba 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Common.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Common.groovy @@ -78,10 +78,18 @@ class Common { return new ClientDslProperty(clientValue) } + ClientDslProperty stub(Object clientValue) { + return new ClientDslProperty(clientValue) + } + ServerDslProperty server(Object serverValue) { return new ServerDslProperty(serverValue) } + ServerDslProperty test(Object serverValue) { + return new ServerDslProperty(serverValue) + } + void assertThatSidesMatch(Pattern pattern, String value) { assert value ==~ pattern } diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy index 6d2ed106d1..4875a1f744 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy @@ -329,6 +329,37 @@ class MockMvcSpockMethodBuilderSpec extends Specification { spockTest.contains('responseBody == "test"') } + @Issue("#127") + def 'should use "stub" as an alias for "client"'() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "GET" + url "test" + } + response { + status 200 + body( + property: value( + stub(123), + test(123) + ) + ) + headers { + header('Content-Type': 'application/json') + + } + + } + } + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains("responseBody.property == 123") + } + @Issue('113') def "should generate regex test for String in response header"() { given: diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy index 1867c49eda..cb0a75255a 100755 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy @@ -1181,6 +1181,42 @@ class WireMockGroovyDslSpec extends WireMockSpec { ''') } + @Issue("#127") + def 'should use "test" as an alias for "server"'() { + given: + GroovyDsl groovyDsl = GroovyDsl.make { + request { + method('POST') + body( + property: value(stub("value"), test("value")) + ) + } + response { + status 200 + } + } + when: + String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() + then: + new JsonSlurper().parseText(wireMockStub) == new JsonSlurper().parseText(''' + { + "request": { + "method": "POST", + "bodyPatterns": [ + { + "equalToJson": "{\\"property\\":\\"value\\"}" + } + ] + }, + "response": { + "status": 200 + } + } + ''') + and: + stubMappingIsValidWireMockStub(wireMockStub) + } + @Issue("#121") def 'should generate stub with empty list as a value of a field'() { given: From 154818c7974ff04a5387072b4424f2e3c71f6f85 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Sat, 22 Aug 2015 10:38:41 +0200 Subject: [PATCH 074/119] [#121] Introduced full JSONPath support - converted all json comparison to JSON Paths - started to use forked Wiremock with support for jsonpath 2.0.0 - changed automatic resolution of Wiremock to support the forked version until WIremock is officially fixed --- .travis.yml | 6 + .../DslToWireMockClientConverterSpec.groovy | 81 ++- .../accurest/SingleTestGenerator.groovy | 11 +- .../accurest/builder/ClassBuilder.groovy | 5 + .../builder/SpockMethodBodyBuilder.groovy | 62 +- .../dsl/BaseWireMockStubStrategy.groovy | 2 +- .../dsl/WireMockRequestStubStrategy.groovy | 201 ++++--- .../dsl/WireMockResponseStubStrategy.groovy | 30 +- .../accurest/dsl/WireMockStubStrategy.groovy | 15 +- .../internal/JsonStructureConverter.groovy | 4 +- .../dsl/internal/MatchingStrategy.groovy | 2 +- .../accurest/util/ContentUtils.groovy | 57 +- .../accurest/util/JsonPathEntry.groovy | 41 ++ .../util/JsonPathJsonConverter.groovy | 153 +++++ .../codearte/accurest/util/JsonPaths.groovy | 23 + ...onConverter.groovy => MapConverter.groovy} | 18 +- .../JaxRsClientSpockMethodBuilderSpec.groovy | 40 +- .../MockMvcSpockMethodBuilderSpec.groovy | 131 ++--- .../dsl/WireMockGroovyDslResponseSpec.groovy | 52 -- .../accurest/dsl/WireMockGroovyDslSpec.groovy | 529 ++++++++---------- .../util/JsonPathJsonConverterSpec.groovy | 199 +++++++ .../plugin/AccurestGradlePlugin.groovy | 16 + .../functionalTest/sampleProject/build.gradle | 1 - build.gradle | 11 +- gradle/release.gradle | 2 +- 25 files changed, 1093 insertions(+), 599 deletions(-) create mode 100644 accurest-core/src/main/groovy/io/codearte/accurest/util/JsonPathEntry.groovy create mode 100644 accurest-core/src/main/groovy/io/codearte/accurest/util/JsonPathJsonConverter.groovy create mode 100644 accurest-core/src/main/groovy/io/codearte/accurest/util/JsonPaths.groovy rename accurest-core/src/main/groovy/io/codearte/accurest/util/{JsonConverter.groovy => MapConverter.groovy} (79%) delete mode 100644 accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslResponseSpec.groovy create mode 100644 accurest-core/src/test/groovy/io/codearte/accurest/util/JsonPathJsonConverterSpec.groovy diff --git a/.travis.yml b/.travis.yml index 7a0dff7140..074a9aabda 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,15 @@ language: java +sudo: false jdk: - oraclejdk7 - openjdk7 - oraclejdk8 +cache: + directories: + - $HOME/.gradle + - $HOME/.m2 + install: ./gradlew assemble script: ./gradlew check --stacktrace --info --continue \ No newline at end of file diff --git a/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/DslToWireMockClientConverterSpec.groovy b/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/DslToWireMockClientConverterSpec.groovy index 0831dde57b..22fc850efe 100755 --- a/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/DslToWireMockClientConverterSpec.groovy +++ b/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/DslToWireMockClientConverterSpec.groovy @@ -1,6 +1,6 @@ package io.codearte.accurest.wiremock -import groovy.json.JsonSlurper +import org.skyscreamer.jsonassert.JSONAssert import spock.lang.Specification class DslToWireMockClientConverterSpec extends Specification { @@ -23,8 +23,9 @@ class DslToWireMockClientConverterSpec extends Specification { when: String json = converter.convertContent(dslBody) then: - new JsonSlurper().parseText(json) == new JsonSlurper().parseText(""" -{"request":{"method":"PUT","urlPattern":"/[0-9]{2}"},"response":{"status":200}}""") + JSONAssert.assertEquals(''' +{"request":{"method":"PUT","urlPattern":"/[0-9]{2}"},"response":{"status":200}} +''', json, false) } @@ -80,22 +81,62 @@ class DslToWireMockClientConverterSpec extends Specification { when: String json = converter.convertContent(dslBody) then: - new JsonSlurper().parseText(json) == new JsonSlurper().parseText("""{ - "request":{ - "method":"PUT", - "url":"/api/12", - "bodyPatterns": [ - { "equalToJson": "[{\\"created_at\\":\\"Sat Jul 26 09:38:57 +0000 2014\\",\\"id\\":492967299297845248,\\"id_str\\":\\"492967299297845248\\",\\"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\\"},\\"text\\":\\"Gonna see you at Warsaw\\"}]" } - ], - "headers": { - "Content-Type": { - "equalTo": "application/vnd.com.ofg.twitter-places-analyzer.v1+json" - } - } - }, - "response":{ - "status":200} - } -""") + JSONAssert.assertEquals(''' +{ + "request" : { + "url" : "/api/12", + "method" : "PUT", + "bodyPatterns" : [ { + "matchesJsonPath" : "$[*].place.bounding_box.coordinates[*][*][?(@ == 38.995548)]" + }, { + "matchesJsonPath" : "$[*].place[?(@.country == 'United States')]" + }, { + "matchesJsonPath" : "$[*].place.bounding_box.coordinates[*][*][?(@ == -77.119759)]" + }, { + "matchesJsonPath" : "$[*].place[?(@.name == 'Washington')]" + }, { + "matchesJsonPath" : "$[*].place.bounding_box[?(@.type == 'Polygon')]" + }, { + "matchesJsonPath" : "$[*][?(@.id_str == '492967299297845248')]" + }, { + "matchesJsonPath" : "$[*].place[?(@.country_code == 'US')]" + }, { + "matchesJsonPath" : "$[*][?(@.id == 492967299297845248)]" + }, { + "matchesJsonPath" : "$[*].place.bounding_box.coordinates[*][*][?(@ == -76.909393)]" + }, { + "matchesJsonPath" : "$[*].place.bounding_box.coordinates[*][*][?(@ == 38.791645)]" + }, { + "matchesJsonPath" : "$[*].place[?(@.id == '01fbe706f872cb32')]" + }, { + "matchesJsonPath" : "$[*].place[?(@.url == 'http://api.twitter.com/1/geo/id/01fbe706f872cb32.json')]" + }, { + "matchesJsonPath" : "$[*].place.bounding_box.coordinates[*][*][?(@ == -77.119759)]" + }, { + "matchesJsonPath" : "$[*].place.bounding_box.coordinates[*][*][?(@ == -76.909393)]" + }, { + "matchesJsonPath" : "$[*].place.bounding_box.coordinates[*][*][?(@ == 38.995548)]" + }, { + "matchesJsonPath" : "$[*][?(@.text == 'Gonna see you at Warsaw')]" + }, { + "matchesJsonPath" : "$[*].place[?(@.place_type == 'city')]" + }, { + "matchesJsonPath" : "$[*][?(@.created_at == 'Sat Jul 26 09:38:57 +0000 2014')]" + }, { + "matchesJsonPath" : "$[*].place[?(@.full_name == 'Washington, DC')]" + }, { + "matchesJsonPath" : "$[*].place.bounding_box.coordinates[*][*][?(@ == 38.791645)]" + } ], + "headers" : { + "Content-Type" : { + "equalTo" : "application/vnd.com.ofg.twitter-places-analyzer.v1+json" + } + } + }, + "response" : { + "status" : 200 + } +} +''', json, false) } } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/SingleTestGenerator.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/SingleTestGenerator.groovy index 2765d7114b..fb2d135419 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/SingleTestGenerator.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/SingleTestGenerator.groovy @@ -50,14 +50,23 @@ class SingleTestGenerator { } if (configProperties.ruleClassForTests) { + clazz.addImport('org.junit.Rule') - .addRule(configProperties.ruleClassForTests) + .addRule(configProperties.ruleClassForTests) } + addJsonPathRelatedImports(clazz) + listOfFiles.each { clazz.addMethod(createTestMethod(it, configProperties)) } return clazz.build() } + private ClassBuilder addJsonPathRelatedImports(ClassBuilder clazz) { + clazz.addImport(['com.jayway.jsonpath.DocumentContext', + 'com.jayway.jsonpath.JsonPath', + 'net.minidev.json.JSONArray']) + } + } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/ClassBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/ClassBuilder.groovy index 29f4fc2c14..3e3a8ce1c0 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/builder/ClassBuilder.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/ClassBuilder.groovy @@ -43,6 +43,11 @@ class ClassBuilder { return this } + ClassBuilder addImport(List importsToAdd) { + imports.addAll(importsToAdd) + return this + } + ClassBuilder addStaticImport(String importToAdd) { staticImports << importToAdd return this diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy index af8993e140..49df619015 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy @@ -3,21 +3,13 @@ import groovy.json.JsonOutput import groovy.transform.PackageScope import groovy.transform.TypeChecked import io.codearte.accurest.dsl.GroovyDsl -import io.codearte.accurest.dsl.internal.DslProperty -import io.codearte.accurest.dsl.internal.ExecutionProperty -import io.codearte.accurest.dsl.internal.MatchingStrategy -import io.codearte.accurest.dsl.internal.QueryParameter -import io.codearte.accurest.dsl.internal.Request -import io.codearte.accurest.dsl.internal.Response +import io.codearte.accurest.dsl.internal.* import io.codearte.accurest.util.ContentType -import io.codearte.accurest.util.JsonConverter - -import java.util.regex.Pattern - -import static io.codearte.accurest.util.ContentUtils.extractValue -import static io.codearte.accurest.util.ContentUtils.recognizeContentTypeFromContent -import static io.codearte.accurest.util.ContentUtils.recognizeContentTypeFromHeader +import io.codearte.accurest.util.MapConverter +import io.codearte.accurest.util.JsonPathJsonConverter +import io.codearte.accurest.util.JsonPaths +import static io.codearte.accurest.util.ContentUtils.* /** * @author Jakub Kubrynski */ @@ -93,17 +85,34 @@ abstract class SpockMethodBodyBuilder { responseBody = extractValue(responseBody, contentType, { DslProperty dslProperty -> dslProperty.serverValue }) } if (contentType == ContentType.JSON) { - bb.addLine("def responseBody = new JsonSlurper().parseText($responseAsString)") + appendJsonPath(bb, responseAsString) + JsonPaths jsonPaths = JsonPathJsonConverter.transformToJsonPathWithTestsSideValues(responseBody) + jsonPaths.each { + it.buildJsonPathComparison('parsedJson').each { + bb.addLine(it) + } + } processBodyElement(bb, "", responseBody) } else if (contentType == ContentType.XML) { bb.addLine("def responseBody = new XmlSlurper().parseText($responseAsString)") // TODO xml validation } else { bb.addLine("def responseBody = ($responseAsString)") - processBodyElement(bb, "", responseBody) + processText(bb, "", responseBody as String) } } + protected void processText(BlockBuilder blockBuilder, String property, String value) { + if (value.startsWith('$')) { + value = value.substring(1).replaceAll('\\$value', "responseBody$property") + blockBuilder.addLine(value) + } else { + blockBuilder.addLine("responseBody$property == \"${value}\"") + } + } + + protected String + protected String getBodyAsString() { Object bodyValue = extractServerValueFromBody(request.body.serverValue) return trimRepeatedQuotes(new JsonOutput().toJson(bodyValue)) @@ -117,7 +126,7 @@ abstract class SpockMethodBodyBuilder { if (bodyValue instanceof GString) { bodyValue = extractValue(bodyValue, { DslProperty dslProperty -> dslProperty.serverValue }) } else { - bodyValue = JsonConverter.transformValues(bodyValue, { it instanceof DslProperty ? it.serverValue : it }) + bodyValue = MapConverter.transformValues(bodyValue, { it instanceof DslProperty ? it.serverValue : it }) } return bodyValue } @@ -147,28 +156,15 @@ abstract class SpockMethodBodyBuilder { } protected void processBodyElement(BlockBuilder blockBuilder, String property, Object value) { - blockBuilder.addLine("responseBody$property == ${value}") + } - protected void processBodyElement(BlockBuilder blockBuilder, String property, String value) { - if (value.startsWith('$')) { - value = value.substring(1).replaceAll('\\$value', "responseBody$property") - blockBuilder.addLine(value) - } else { - blockBuilder.addLine("responseBody$property == '''${value}'''") - } - } - - protected void processBodyElement(BlockBuilder blockBuilder, String property, Pattern pattern) { - blockBuilder.addLine("responseBody$property ==~ java.util.regex.Pattern.compile('${pattern.pattern()}')") - } - - protected void processBodyElement(BlockBuilder blockBuilder, String property, DslProperty dslProperty) { - processBodyElement(blockBuilder, property, dslProperty.serverValue) + protected void appendJsonPath(BlockBuilder blockBuilder, String json) { + blockBuilder.addLine("DocumentContext parsedJson = JsonPath.parse($json)") } protected void processBodyElement(BlockBuilder blockBuilder, String property, ExecutionProperty exec) { - blockBuilder.addLine("${exec.insertValue("responseBody$property")}") + blockBuilder.addLine("${exec.insertValue("parsedJson.read('\\\$$property')")}") } protected void processBodyElement(BlockBuilder blockBuilder, String property, Map.Entry entry) { diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/BaseWireMockStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/BaseWireMockStubStrategy.groovy index 0afb66273b..c69e5539fa 100755 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/BaseWireMockStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/BaseWireMockStubStrategy.groovy @@ -10,7 +10,7 @@ import io.codearte.accurest.util.ContentType import java.util.regex.Pattern import static io.codearte.accurest.util.ContentUtils.extractValue -import static io.codearte.accurest.util.JsonConverter.transformValues +import static io.codearte.accurest.util.MapConverter.transformValues @TypeChecked abstract class BaseWireMockStubStrategy { diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockRequestStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockRequestStubStrategy.groovy index d540069484..923b614f7a 100755 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockRequestStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockRequestStubStrategy.groovy @@ -1,21 +1,21 @@ package io.codearte.accurest.dsl +import com.github.tomakehurst.wiremock.http.RequestMethod +import com.github.tomakehurst.wiremock.matching.RequestPattern +import com.github.tomakehurst.wiremock.matching.ValuePattern +import groovy.json.JsonOutput import groovy.transform.PackageScope import groovy.transform.TypeChecked -import io.codearte.accurest.dsl.internal.Body -import io.codearte.accurest.dsl.internal.ClientRequest -import io.codearte.accurest.dsl.internal.DslProperty -import io.codearte.accurest.dsl.internal.MatchingStrategy -import io.codearte.accurest.dsl.internal.QueryParameter -import io.codearte.accurest.dsl.internal.QueryParameters -import io.codearte.accurest.dsl.internal.Request +import groovy.transform.TypeCheckingMode +import io.codearte.accurest.dsl.internal.* import io.codearte.accurest.util.ContentType +import io.codearte.accurest.util.ContentUtils +import io.codearte.accurest.util.JsonPathJsonConverter +import io.codearte.accurest.util.JsonPaths +import io.codearte.accurest.util.MapConverter import java.util.regex.Pattern -import static io.codearte.accurest.util.ContentUtils.getEqualsTypeFromContentType -import static io.codearte.accurest.util.ContentUtils.recognizeContentTypeFromContent -import static io.codearte.accurest.util.ContentUtils.recognizeContentTypeFromHeader -import static io.codearte.accurest.util.ContentUtils.recognizeContentTypeFromMatchingStrategy +import static io.codearte.accurest.util.ContentUtils.* import static io.codearte.accurest.util.RegexpBuilders.buildGStringRegexpMatch import static io.codearte.accurest.util.RegexpBuilders.buildJSONRegexpMatch @@ -30,74 +30,144 @@ class WireMockRequestStubStrategy extends BaseWireMockStubStrategy { } @PackageScope - Map buildClientRequestContent() { - return buildRequestContent(new ClientRequest(request)) + RequestPattern buildClientRequestContent() { + RequestPattern requestPattern = new RequestPattern() + appendMethod(requestPattern) + appendHeaders(requestPattern) + appendUrl(requestPattern) + appendQueryParameters(requestPattern) + appendBody(requestPattern) + return requestPattern } - private Map buildRequestContent(ClientRequest request) { - return ([method : request?.method?.clientValue, - headers : buildClientRequestHeadersSection(request.headers) - ] << appendUrl(request) << appendQueryParameters(request) << appendBody(request)).findAll { it.value } + private void appendMethod(RequestPattern requestPattern) { + if(!request.method) { + return + } + requestPattern.setMethod(RequestMethod.fromString(request.method.clientValue?.toString())) } - private Map appendUrl(ClientRequest clientRequest) { - Object urlPath = clientRequest?.urlPath?.clientValue + private void appendBody(RequestPattern requestPattern) { + if (!request.body) { + return + } + ContentType contentType = tryToGetContentType() + if (contentType == ContentType.JSON) { + JsonPaths values = JsonPathJsonConverter.transformToJsonPathWithStubsSideValues(getMatchingStrategyFromBody(request.body)?.clientValue) + if (values.empty) { + requestPattern.bodyPatterns = [new ValuePattern(jsonCompareMode: org.skyscreamer.jsonassert.JSONCompareMode.LENIENT, + equalToJson: JsonOutput.toJson(getMatchingStrategy(request.body.clientValue).clientValue) ) ] + } else { + requestPattern.bodyPatterns = values.collect { new ValuePattern(matchesJsonPath: it.jsonPath) } ?: null + } + } else if (contentType == ContentType.XML) { + requestPattern.bodyPatterns = [new ValuePattern(equalToXml: getMatchingStrategy(request.body.clientValue).clientValue.toString())] + } else if (containsPattern(request?.body)) { + MatchingStrategy matchingStrategy = appendBodyRegexpMatchPattern(request.body) + requestPattern.bodyPatterns = [convertToValuePattern(matchingStrategy)] + } else { + requestPattern.bodyPatterns = [convertToValuePattern(getMatchingStrategy(request.body.clientValue))] + } + } + + private ContentType tryToGetContentType() { + ContentType contentType = recognizeContentTypeFromHeader(request.headers) + if (contentType == ContentType.UNKNOWN) { + if (!request.body.clientValue) { + return ContentType.UNKNOWN + } + return ContentUtils.getClientContentType(request.body.clientValue) + } + return contentType + } + + private void appendHeaders(RequestPattern requestPattern) { + if(!request.headers) { + return + } + request.headers.entries.each { + requestPattern.addHeader(it.name, convertToValuePattern(it.clientValue)) + } + } + + private void appendUrl(RequestPattern requestPattern) { + Object urlPath = request?.urlPath?.clientValue if (urlPath) { - return [urlPath: urlPath] + requestPattern.setUrlPath(urlPath.toString()) } - Object url = clientRequest?.url?.clientValue - return url instanceof Pattern ? [urlPattern: url.pattern()] : [url: url] - } - - private Map appendQueryParameters(ClientRequest clientRequest) { - QueryParameters queryParameters = clientRequest?.urlPath?.queryParameters ?: clientRequest?.url?.queryParameters - return queryParameters && !queryParameters.parameters.isEmpty() ? - [queryParameters: buildUrlPathQueryParameters(queryParameters)] : [:] - } - - private Map buildUrlPathQueryParameters(QueryParameters queryParameters) { - return queryParameters.parameters.collectEntries { QueryParameter param -> - parseQueryParameter(param.name, param.clientValue) + if(!request.url) { + return + } + Object url = request?.url?.clientValue + if(url instanceof Pattern) { + requestPattern.setUrlPattern(url.pattern()) + } else { + requestPattern.setUrl(url.toString()) } } - protected Map parseQueryParameter(String name, MatchingStrategy matchingStrategy) { - return buildQueryParameter(name, matchingStrategy.clientValue, matchingStrategy.type) + private void appendQueryParameters(RequestPattern requestPattern) { + QueryParameters queryParameters = request?.urlPath?.queryParameters ?: request?.url?.queryParameters + queryParameters?.parameters?.each { + requestPattern.addQueryParam(it.name, convertToValuePattern(it.clientValue)) + } } - protected Map parseQueryParameter(String name, Object value) { - return buildQueryParameter(name, value, MatchingStrategy.Type.EQUAL_TO) + @TypeChecked(TypeCheckingMode.SKIP) + private static ValuePattern convertToValuePattern(Object object) { + switch (object) { + case Pattern: + Pattern value = object as Pattern + return ValuePattern.matches(value.pattern()) + case MatchingStrategy: + MatchingStrategy value = object as MatchingStrategy + switch (value.type) { + case MatchingStrategy.Type.NOT_MATCHING: + return new ValuePattern(doesNotMatch: value.clientValue) + case MatchingStrategy.Type.ABSENT: + return ValuePattern.absent() + default: + return ValuePattern."${value.type.name}"(value.clientValue) + } + default: + return ValuePattern.equalTo(object.toString()) + } } - protected Map parseQueryParameter(String name, Pattern pattern) { - return buildQueryParameter(name, pattern.pattern(), MatchingStrategy.Type.MATCHING) + private MatchingStrategy getMatchingStrategyFromBody(Body body) { + if(!body) { + return null + } + return getMatchingStrategy(body.clientValue) } - private Map buildQueryParameter(String name, Pattern pattern, MatchingStrategy.Type type) { - return buildQueryParameter(name, pattern.pattern(), type) + private MatchingStrategy getMatchingStrategy(MatchingStrategy matchingStrategy) { + return getMatchingStrategyIncludingContentType(matchingStrategy) + } + private MatchingStrategy getMatchingStrategy(GString gString) { + if (!gString) { + return new MatchingStrategy("", MatchingStrategy.Type.EQUAL_TO) + } + def extractedValue = ContentUtils.extractValue(gString) { + it instanceof DslProperty ? it.clientValue : getStringFromGString(it) + } + def value = getStringFromGString(extractedValue) + return getMatchingStrategy(value) } - private Map buildQueryParameter(String name, Object value, MatchingStrategy.Type type) { - return [(name): [(type.name) : value]] + private def getStringFromGString(Object object) { + return object instanceof GString ? object.toString() : object } - private Map appendBody(ClientRequest clientRequest) { - return clientRequest.body? appendBody(clientRequest.body) : [:] + private MatchingStrategy getMatchingStrategy(Object bodyValue) { + return tryToFindMachingStrategy(bodyValue) } - private Map appendBody(Body body) { - return [bodyPatterns: (appendBodyPatterns(body.clientValue))] + private MatchingStrategy tryToFindMachingStrategy(Object bodyValue) { + return new MatchingStrategy(MapConverter.transformToClientValues(bodyValue), getEqualsTypeFromContentTypeHeader()) } - private List> appendBodyPatterns(MatchingStrategy matchingStrategy) { - return [appendBodyPattern(matchingStrategy)] - } - - private List> appendBodyPatterns(Object bodyValue) { - return appendBodyPatterns(new MatchingStrategy(bodyValue, getEqualsTypeFromContentTypeHeader())) - } - - private Map appendBodyPattern(MatchingStrategy matchingStrategy) { + private MatchingStrategy getMatchingStrategyIncludingContentType(MatchingStrategy matchingStrategy) { MatchingStrategy.Type type = matchingStrategy.type Object value = matchingStrategy.clientValue ContentType contentType = recognizeContentTypeFromMatchingStrategy(type) @@ -105,29 +175,22 @@ class WireMockRequestStubStrategy extends BaseWireMockStubStrategy { contentType = recognizeContentTypeFromContent(value) type = getEqualsTypeFromContentType(contentType) } - if (containsPattern(value)) { - return appendBodyRegexpMatchPattern(value, contentType) - } - return buildMatchPattern(new MatchingStrategy(parseBody(value, contentType), type)) + return new MatchingStrategy(parseBody(value, contentType), type) } - private Map appendBodyRegexpMatchPattern(Object value, ContentType contentType) { + private MatchingStrategy appendBodyRegexpMatchPattern(Object value, ContentType contentType) { switch (contentType) { case ContentType.JSON: - return buildMatchPattern(new MatchingStrategy(buildJSONRegexpMatch(value), MatchingStrategy.Type.MATCHING)) + return new MatchingStrategy(buildJSONRegexpMatch(value), MatchingStrategy.Type.MATCHING) case ContentType.UNKNOWN: - return buildMatchPattern(new MatchingStrategy(buildGStringRegexpMatch(value), MatchingStrategy.Type.MATCHING)) + return new MatchingStrategy(buildGStringRegexpMatch(value), MatchingStrategy.Type.MATCHING) case ContentType.XML: throw new IllegalStateException("XML pattern matching is not implemented yet") } } - private Map buildMatchPattern(MatchingStrategy matchingStrategy) { - Map result = [(matchingStrategy.type.name): matchingStrategy.clientValue.toString()] - if (matchingStrategy.type == MatchingStrategy.Type.EQUAL_TO_JSON && matchingStrategy.jsonCompareMode) { - return result << [jsonCompareMode : (matchingStrategy.jsonCompareMode.toString())] - } - return result + private MatchingStrategy appendBodyRegexpMatchPattern(Object value) { + return appendBodyRegexpMatchPattern(value, ContentType.UNKNOWN) } private boolean containsPattern(GString bodyAsValue) { diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockResponseStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockResponseStubStrategy.groovy index 3cb9a58155..5af5898257 100755 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockResponseStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockResponseStubStrategy.groovy @@ -1,7 +1,9 @@ package io.codearte.accurest.dsl +import com.github.tomakehurst.wiremock.http.HttpHeader +import com.github.tomakehurst.wiremock.http.HttpHeaders +import com.github.tomakehurst.wiremock.http.ResponseDefinition import groovy.transform.PackageScope import groovy.transform.TypeChecked -import io.codearte.accurest.dsl.internal.ClientResponse import io.codearte.accurest.dsl.internal.Request import io.codearte.accurest.dsl.internal.Response import io.codearte.accurest.util.ContentType @@ -22,23 +24,31 @@ class WireMockResponseStubStrategy extends BaseWireMockStubStrategy { } @PackageScope - Map buildClientResponseContent() { - return buildResponseContent(new ClientResponse(response)) + ResponseDefinition buildClientResponseContent() { + ResponseDefinition responseDefinition = new ResponseDefinition() + responseDefinition.setStatus(response.status.clientValue as Integer) + appendHeaders(responseDefinition) + appendBody(responseDefinition) + return responseDefinition } - private Map buildResponseContent(ClientResponse response) { - return ([status : response?.status?.clientValue, - headers: buildClientResponseHeadersSection(response.headers) - ] << appendBody(response)).findAll { it.value } + private void appendHeaders(ResponseDefinition responseDefinition) { + if(!(response.headers)) { + return + } + responseDefinition.setHeaders(new HttpHeaders(response.headers.entries?.collect { new HttpHeader(it.name, it.clientValue.toString()) })) } - private Map appendBody(ClientResponse response) { - Object body = response?.body?.clientValue + private void appendBody(ResponseDefinition responseDefinition) { + if (!response.body) { + return + } + Object body = response.body.clientValue ContentType contentType = recognizeContentTypeFromHeader(response.headers) if (contentType == ContentType.UNKNOWN) { contentType = recognizeContentTypeFromContent(body) } - return body != null ? [body: parseBody(body, contentType)] : [:] + responseDefinition.setBody(parseBody(body, contentType)) } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockStubStrategy.groovy index acff79a11a..337f7b8557 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockStubStrategy.groovy @@ -1,6 +1,8 @@ package io.codearte.accurest.dsl -import groovy.json.JsonOutput +import com.github.tomakehurst.wiremock.http.ResponseDefinition +import com.github.tomakehurst.wiremock.matching.RequestPattern +import com.github.tomakehurst.wiremock.stubbing.StubMapping import groovy.transform.CompileDynamic import groovy.transform.CompileStatic @@ -19,11 +21,14 @@ class WireMockStubStrategy { @CompileDynamic String toWireMockClientStub() { - def wiremockStubDefinition = [request : wireMockRequestStubStrategy.buildClientRequestContent(), - response: wireMockResponseStubStrategy.buildClientResponseContent()] + StubMapping stubMapping = new StubMapping() + RequestPattern request = wireMockRequestStubStrategy.buildClientRequestContent() + ResponseDefinition response = wireMockResponseStubStrategy.buildClientResponseContent() if (priority) { - wiremockStubDefinition.priority = priority + stubMapping.priority = priority } - return JsonOutput.prettyPrint(JsonOutput.toJson(wiremockStubDefinition)) + stubMapping.request = request + stubMapping.response = response + return StubMapping.buildJsonStringFor(stubMapping) } } \ No newline at end of file diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/JsonStructureConverter.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/JsonStructureConverter.groovy index f5860bfaad..b6b6f79eee 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/JsonStructureConverter.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/JsonStructureConverter.groovy @@ -2,7 +2,7 @@ package io.codearte.accurest.dsl.internal import groovy.json.JsonOutput import groovy.transform.CompileStatic -import io.codearte.accurest.util.JsonConverter +import io.codearte.accurest.util.MapConverter import java.util.regex.Pattern @@ -17,7 +17,7 @@ class JsonStructureConverter { Closure performAdditionalLogicOnSerializedJson, Closure convertSerializedJsonToSth) { LinkedList queue = new LinkedList<>() - def transformedJson = JsonConverter.transformValues(parsedJson, { + def transformedJson = MapConverter.transformValues(parsedJson, { if(retrievePlaceholders(it)) { queue.push(it) return TEMPORARY_PLACEHOLDER diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/MatchingStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/MatchingStrategy.groovy index c31c0b4bf6..191af8d7ee 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/MatchingStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/MatchingStrategy.groovy @@ -34,7 +34,7 @@ class MatchingStrategy extends DslProperty { enum Type { - EQUAL_TO("equalTo"), CONTAINS("contains"), MATCHING("matches"), NOT_MATCHING("doesNotMatch"), + EQUAL_TO("equalTo"), CONTAINS("containing"), MATCHING("matches"), NOT_MATCHING("doesNotMatch"), EQUAL_TO_JSON("equalToJson"), EQUAL_TO_XML("equalToXml"), ABSENT("absent") final String name diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy index b42cadcf5c..a7e66f9ec3 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy @@ -1,5 +1,6 @@ package io.codearte.accurest.util import groovy.json.JsonException +import groovy.json.JsonOutput import groovy.json.JsonSlurper import groovy.transform.TypeChecked import groovy.util.logging.Slf4j @@ -18,6 +19,10 @@ import static org.apache.commons.lang3.StringEscapeUtils.escapeXml11 @Slf4j class ContentUtils { + public static final Closure GET_STUB_SIDE = { + it instanceof DslProperty ? it.clientValue : it + } + private static final Pattern TEMPORARY_PATTERN_HOLDER = Pattern.compile('REGEXP>>(.*)<<') private static final String JSON_VALUE_PATTERN_FOR_REGEX = 'REGEXP>>%s<<' @@ -57,7 +62,57 @@ class ContentUtils { return extractValueForGString(bodyAsValue, valueProvider) } } + } + public static ContentType getClientContentType(GString bodyAsValue) { + try { + extractValueForJSON(bodyAsValue, GET_STUB_SIDE) + return ContentType.JSON + } catch(JsonException e) { + try { + new XmlSlurper().parseText(extractValueForXML(bodyAsValue, GET_STUB_SIDE).toString()) + return ContentType.XML + } catch (Exception exception) { + extractValueForGString(bodyAsValue, GET_STUB_SIDE) + return ContentType.UNKNOWN + } + } + } + + public static ContentType getClientContentType(String bodyAsValue) { + try { + new JsonSlurper().parseText(bodyAsValue) + return ContentType.JSON + } catch(JsonException e) { + try { + new XmlSlurper().parseText(bodyAsValue) + return ContentType.XML + } catch (Exception exception) { + return ContentType.UNKNOWN + } + } + } + + public static ContentType getClientContentType(Object bodyAsValue) { + return ContentType.UNKNOWN + } + + public static ContentType getClientContentType(Map bodyAsValue) { + try { + JsonOutput.toJson(bodyAsValue) + return ContentType.JSON + } catch (Exception ignore) { + return ContentType.UNKNOWN + } + } + + public static ContentType getClientContentType(List bodyAsValue) { + try { + JsonOutput.toJson(bodyAsValue) + return ContentType.JSON + } catch (Exception ignore) { + return ContentType.UNKNOWN + } } private static GStringImpl extractValueForGString(GString bodyAsValue, Closure valueProvider) { @@ -108,7 +163,7 @@ class ContentUtils { } private static Object convertAllTemporaryRegexPlaceholdersBackToPatterns(parsedJson) { - JsonConverter.transformValues(parsedJson, { Object value -> + MapConverter.transformValues(parsedJson, { Object value -> if (value instanceof String) { String string = (String) value Matcher matcher = TEMPORARY_PATTERN_HOLDER.matcher(string) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonPathEntry.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonPathEntry.groovy new file mode 100644 index 0000000000..0e0f928f22 --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonPathEntry.groovy @@ -0,0 +1,41 @@ +package io.codearte.accurest.util + +import java.util.regex.Pattern + +class JsonPathEntry { + final String jsonPath + final String optionalSuffix + final Object value + + JsonPathEntry(String jsonPath, String optionalSuffix, Object value) { + this.jsonPath = jsonPath + this.optionalSuffix = optionalSuffix + this.value = value + } + + List buildJsonPathComparison(String parsedJsonVariable) { + if (optionalSuffix) { + return ["!${parsedJsonVariable}.read('''${jsonPath}''', JSONArray).empty"] + } else if (traversesOverCollections()) { + return ["${parsedJsonVariable}.read('''${jsonPath}''', JSONArray).size() == 1", + "${parsedJsonVariable}.read('''${jsonPath}''', JSONArray).get(0) ${operator()} ${potentiallyWrappedWithQuotesValue()}"] + } + return ["${parsedJsonVariable}.read('''${jsonPath}''') ${operator()} ${potentiallyWrappedWithQuotesValue()}"] + } + + private boolean traversesOverCollections() { + return jsonPath.contains('[*]') + } + + String operator() { + return value instanceof Pattern ? "==~" : "==" + } + + String potentiallyWrappedWithQuotesValue() { + return value instanceof Number ? value : "'''$value'''" + } + + static JsonPathEntry simple(String jsonPath, Object value) { + return new JsonPathEntry(jsonPath, "", value) + } +} diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonPathJsonConverter.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonPathJsonConverter.groovy new file mode 100644 index 0000000000..28625a1cbf --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonPathJsonConverter.groovy @@ -0,0 +1,153 @@ +package io.codearte.accurest.util + +import groovy.json.JsonSlurper +import io.codearte.accurest.dsl.internal.DslProperty +import io.codearte.accurest.dsl.internal.ExecutionProperty + +import java.util.regex.Pattern +/** + * @author Marcin Grzejszczak + */ +class JsonPathJsonConverter { + + private static final Boolean SERVER_SIDE = false + private static final Boolean CLIENT_SIDE = true + + public static final String ROOT_JSON_PATH_ELEMENT = '$' + public static final String ALL_ELEMENTS = "[*]" + + public static JsonPaths transformToJsonPathWithTestsSideValues(def json) { + return transformToJsonPathWithValues(json, SERVER_SIDE) + } + + public static JsonPaths transformToJsonPathWithStubsSideValues(def json) { + return transformToJsonPathWithValues(json, CLIENT_SIDE) + } + + private static JsonPaths transformToJsonPathWithValues(def json, boolean clientSide) { + if(!json) { + return new JsonPaths() + } + JsonPaths pathsAndValues = [] as Set + Object convertedJson = getClientOrServerSideValues(json, clientSide) + traverseRecursivelyForKey(convertedJson, ROOT_JSON_PATH_ELEMENT) { String key, Object value -> + if (value instanceof ExecutionProperty) { + return + } + JsonPathEntry entry = getValueToInsert(key, value) + pathsAndValues.add(entry) + } + return pathsAndValues + } + + private static Object getClientOrServerSideValues(json, boolean clientSide) { + return MapConverter.transformValues(json) { + boolean dslProp = it instanceof DslProperty + if (dslProp) { + DslProperty dslProperty = ((DslProperty) it) + return clientSide ? + getClientOrServerSideValues(dslProperty.clientValue, clientSide) : getClientOrServerSideValues(dslProperty.serverValue, clientSide) + } + return it + } + } + + protected static def traverseRecursively(Class parentType, String key, def value, Closure closure) { + if (value instanceof String && value) { + try { + def json = new JsonSlurper().parseText(value) + if (json instanceof Map) { + return convertWithKey(parentType, key, json, closure) + } + } catch (Exception ignore) { + return closure(key, value) + } + } else if (isAnEntryWithNonCollectionLikeValue(value)) { + return convertWithKey(List, key, value as Map, closure) + } else if (isAnEntryWithoutNestedStructures(value)) { + return convertWithKey(List, key, value as Map, closure) + } else if (value instanceof Map) { + return convertWithKey(Map, key, value as Map, closure) + } else if (value instanceof List) { + value.each { def element -> + traverseRecursively(List, "$key[*]", element, closure) + } + return value + } + try { + return closure(key, value) + } catch (Exception ignore) { + return value + } + } + + private static boolean isAnEntryWithNonCollectionLikeValue(def value) { + if (!(value instanceof Map)) { + return false + } + Map valueAsMap = ((Map) value) + boolean mapHasOneEntry = valueAsMap.size() == 1 + if (!mapHasOneEntry) { + return false + } + Object valueOfEntry = valueAsMap.entrySet().first().value + return !(valueOfEntry instanceof Map || valueOfEntry instanceof List) + } + + private static boolean isAnEntryWithoutNestedStructures(def value) { + if (!(value instanceof Map)) { + return false + } + Map valueAsMap = ((Map) value) + return valueAsMap.entrySet().every { Map.Entry entry -> + [String, Number].any { entry.value.getClass().isAssignableFrom(it) } + } + } + + private static Map convertWithKey(Class parentType, String parentKey, Map map, Closure closureToExecute) { + return map.collectEntries { + String entrykey, value -> + [entrykey, traverseRecursively(parentType, "${parentKey}.${entrykey}", value, closureToExecute)] + } + } + + private static void traverseRecursivelyForKey(def json, String rootKey, Closure closure) { + traverseRecursively(Map, rootKey, json, closure) + } + + private static JsonPathEntry getValueToInsert(String key, Object value) { + return convertToListElementFiltering(key, value) + } + + protected static JsonPathEntry convertToListElementFiltering(String key, Object value) { + if (key.endsWith(ALL_ELEMENTS)) { + int lastAllElements = key.lastIndexOf(ALL_ELEMENTS) + String keyWithoutAllElements = key.substring(0, lastAllElements) + return JsonPathEntry.simple("""$keyWithoutAllElements[?(@ ${compareWith(value)})]""".toString(), value) + } + return getKeyForTraversalOfListWithNonPrimitiveTypes(key, value) + } + + private static JsonPathEntry getKeyForTraversalOfListWithNonPrimitiveTypes(String key, Object value) { + int lastDot = key.lastIndexOf('.') + String keyWithoutLastElement = key.substring(0, lastDot) + String lastElement = key.substring(lastDot + 1).replaceAll(~/\[\*\]/, "") + return new JsonPathEntry( + """$keyWithoutLastElement[?(@.$lastElement ${compareWith(value)})]""".toString(), + lastElement, + value + ) + } + + protected static String compareWith(Object value) { + if (value instanceof Pattern) { + return """=~ /${(value as Pattern).pattern()}/""" + } + return """== ${potentiallyWrappedWithQuotesValue(value)}""" + } + + protected static String potentiallyWrappedWithQuotesValue(Object value) { + return value instanceof Number ? value : "'$value'" + } + +} diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonPaths.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonPaths.groovy new file mode 100644 index 0000000000..6c4aad1214 --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonPaths.groovy @@ -0,0 +1,23 @@ +package io.codearte.accurest.util + +class JsonPaths extends HashSet { + + Object getAt(String key) { + return find { + it.jsonPath == key + }?.value + } + + Object putAt(String key, Object value) { + JsonPathEntry entry = find { + it.jsonPath == key + } + if (!entry) { + return null + } + Object oldValue = entry.value + add(new JsonPathEntry(entry.jsonPath, entry.optionalSuffix, value)) + return oldValue + } +} + diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonConverter.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/MapConverter.groovy similarity index 79% rename from accurest-core/src/main/groovy/io/codearte/accurest/util/JsonConverter.groovy rename to accurest-core/src/main/groovy/io/codearte/accurest/util/MapConverter.groovy index 208a3aefa3..14568e7584 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonConverter.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/MapConverter.groovy @@ -1,15 +1,16 @@ package io.codearte.accurest.util import groovy.json.JsonSlurper +import io.codearte.accurest.dsl.internal.DslProperty + /** * @author Marcin Grzejszczak */ -class JsonConverter { +class MapConverter { - private static Map convert(Map map, Closure closure) { - return map.collectEntries { - key, value -> - [key, transformValues(value, closure)] + static def transformToClientValues(def value) { + return transformValues(value) { + it instanceof DslProperty ? it.clientValue : it } } @@ -35,4 +36,11 @@ class JsonConverter { } } + private static Map convert(Map map, Closure closure) { + return map.collectEntries { + key, value -> + [key, transformValues(value, closure)] + } + } + } diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodBuilderSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodBuilderSpec.groovy index 07123d6265..8bd272b7ac 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodBuilderSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodBuilderSpec.groovy @@ -26,8 +26,8 @@ class JaxRsClientSpockMethodBuilderSpec extends Specification { when: builder.appendTo(blockBuilder) then: - blockBuilder.toString().contains("responseBody.property1 == '''a'''") - blockBuilder.toString().contains("responseBody.property2 == '''b'''") + blockBuilder.toString().contains("\$[?(@.property1 == 'a')]") + blockBuilder.toString().contains("\$[?(@.property2 == 'b')]") } @Issue("#79") @@ -54,9 +54,9 @@ class JaxRsClientSpockMethodBuilderSpec extends Specification { when: builder.appendTo(blockBuilder) then: - blockBuilder.toString().contains("responseBody.property1 == '''a'''") - blockBuilder.toString().contains("responseBody.property2[0].a == '''sth'''") - blockBuilder.toString().contains("responseBody.property2[1].b == '''sthElse'''") + blockBuilder.toString().contains("\$[?(@.property1 == 'a')]") + blockBuilder.toString().contains("\$.property2[*][?(@.a == 'sth')]") + blockBuilder.toString().contains("\$.property2[*][?(@.b == 'sthElse')]") } @Issue("#82") @@ -128,8 +128,8 @@ class JaxRsClientSpockMethodBuilderSpec extends Specification { when: builder.appendTo(blockBuilder) then: - blockBuilder.toString().contains("responseBody[0].property1 == '''a'''") - blockBuilder.toString().contains("responseBody[1].property2 == '''b'''") + blockBuilder.toString().contains("\$[*][?(@.property1 == 'a')]") + blockBuilder.toString().contains("\$[*][?(@.property2 == 'b')]") } def "should generate assertions for array inside response body element"() { @@ -154,8 +154,8 @@ class JaxRsClientSpockMethodBuilderSpec extends Specification { when: builder.appendTo(blockBuilder) then: - blockBuilder.toString().contains("responseBody.property1[0].property2 == '''test1'''") - blockBuilder.toString().contains("responseBody.property1[1].property3 == '''test2'''") + blockBuilder.toString().contains("\$.property1[*][?(@.property3 == 'test2')]") + blockBuilder.toString().contains("\$.property1[*][?(@.property2 == 'test1')]") } def "should generate assertions for nested objects in response body"() { @@ -180,8 +180,8 @@ class JaxRsClientSpockMethodBuilderSpec extends Specification { when: builder.appendTo(blockBuilder) then: - blockBuilder.toString().contains("responseBody.property1 == '''a'''") - blockBuilder.toString().contains("responseBody.property2.property3 == '''b'''") + blockBuilder.toString().contains("\$.property2[?(@.property3 == 'b')]") + blockBuilder.toString().contains("\$[?(@.property1 == 'a')]") } def "should generate regex assertions for map objects in response body"() { @@ -212,8 +212,8 @@ class JaxRsClientSpockMethodBuilderSpec extends Specification { when: builder.appendTo(blockBuilder) then: - blockBuilder.toString().contains("responseBody.property1 == '''a'''") - blockBuilder.toString().contains("responseBody.property2 ==~ java.util.regex.Pattern.compile('[0-9]{3}')") + blockBuilder.toString().contains("\$[?(@.property2 =~ /[0-9]{3}/)]") + blockBuilder.toString().contains("\$[?(@.property1 == 'a')]") } def "should generate regex assertions for string objects in response body"() { @@ -238,8 +238,8 @@ class JaxRsClientSpockMethodBuilderSpec extends Specification { when: builder.appendTo(blockBuilder) then: - blockBuilder.toString().contains("responseBody.property1 == '''a'''") - blockBuilder.toString().contains("responseBody.property2 ==~ java.util.regex.Pattern.compile('[0-9]{3}')") + blockBuilder.toString().contains("\$[?(@.property2 =~ /[0-9]{3}/)]") + blockBuilder.toString().contains("\$[?(@.property1 == 'a')]") } def "should ignore 'Accept' header and use 'request' method"() { @@ -335,8 +335,8 @@ class JaxRsClientSpockMethodBuilderSpec extends Specification { spockTest.contains("queryParam('age', '99'") spockTest.contains("queryParam('name', 'Denis.Stepanov'") spockTest.contains("queryParam('email', 'bob@email.com'") - spockTest.contains("responseBody.property1 == '''a'''") - spockTest.contains("responseBody.property2 == '''b'''") + spockTest.contains('$[?(@.property2 == \'b\')]') + spockTest.contains('$[?(@.property1 == \'a\')]') } def "should generate test for empty body"() { @@ -372,12 +372,14 @@ class JaxRsClientSpockMethodBuilderSpec extends Specification { body "test" } } - JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl) + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) def spockTest = blockBuilder.toString() then: - spockTest.contains("responseBody == '''test'''") + spockTest.contains('def responseBody = (response.body.asString())') + spockTest.contains('responseBody == "test"') } + } diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy index df36f3058f..dcbf988168 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy @@ -3,15 +3,13 @@ package io.codearte.accurest.builder import io.codearte.accurest.dsl.GroovyDsl import spock.lang.Issue import spock.lang.Specification -import spock.lang.Unroll /** * @author Jakub Kubrynski */ class MockMvcSpockMethodBuilderSpec extends Specification { - @Unroll - def "should generate assertions for simple response body for [#spockMethodBuilder]"() { + def "should generate assertions for simple response body"() { given: GroovyDsl contractDsl = GroovyDsl.make { request { @@ -26,13 +24,13 @@ class MockMvcSpockMethodBuilderSpec extends Specification { }""" } } - SpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) then: - blockBuilder.toString().contains("responseBody.property1 == '''a'''") - blockBuilder.toString().contains("responseBody.property2 == '''b'''") + blockBuilder.toString().contains("\$[?(@.property1 == 'a')]") + blockBuilder.toString().contains("\$[?(@.property2 == 'b')]") } @Issue("#79") @@ -54,14 +52,14 @@ class MockMvcSpockMethodBuilderSpec extends Specification { ) } } - SpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) then: - blockBuilder.toString().contains("responseBody.property1 == '''a'''") - blockBuilder.toString().contains("responseBody.property2[0].a == '''sth'''") - blockBuilder.toString().contains("responseBody.property2[1].b == '''sthElse'''") + blockBuilder.toString().contains("\$[?(@.property1 == 'a')]") + blockBuilder.toString().contains("\$.property2[*][?(@.a == 'sth')]") + blockBuilder.toString().contains("\$.property2[*][?(@.b == 'sthElse')]") } @Issue("#82") @@ -79,7 +77,7 @@ class MockMvcSpockMethodBuilderSpec extends Specification { status 200 } } - SpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) @@ -102,7 +100,7 @@ class MockMvcSpockMethodBuilderSpec extends Specification { status 200 } } - SpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) @@ -128,13 +126,13 @@ class MockMvcSpockMethodBuilderSpec extends Specification { }]""" } } - SpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) then: - blockBuilder.toString().contains("responseBody[0].property1 == '''a'''") - blockBuilder.toString().contains("responseBody[1].property2 == '''b'''") + blockBuilder.toString().contains("\$[*][?(@.property1 == 'a')]") + blockBuilder.toString().contains("\$[*][?(@.property2 == 'b')]") } def "should generate assertions for array inside response body element"() { @@ -154,13 +152,13 @@ class MockMvcSpockMethodBuilderSpec extends Specification { }""" } } - SpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) then: - blockBuilder.toString().contains("responseBody.property1[0].property2 == '''test1'''") - blockBuilder.toString().contains("responseBody.property1[1].property3 == '''test2'''") + blockBuilder.toString().contains("\$.property1[*][?(@.property3 == 'test2')]") + blockBuilder.toString().contains("\$.property1[*][?(@.property2 == 'test1')]") } def "should generate assertions for nested objects in response body"() { @@ -180,13 +178,13 @@ class MockMvcSpockMethodBuilderSpec extends Specification { ''' } } - SpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) then: - blockBuilder.toString().contains("responseBody.property1 == '''a'''") - blockBuilder.toString().contains("responseBody.property2.property3 == '''b'''") + blockBuilder.toString().contains("\$.property2[?(@.property3 == 'b')]") + blockBuilder.toString().contains("\$[?(@.property1 == 'a')]") } def "should generate regex assertions for map objects in response body"() { @@ -212,13 +210,13 @@ class MockMvcSpockMethodBuilderSpec extends Specification { } } - SpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) then: - blockBuilder.toString().contains("responseBody.property1 == '''a'''") - blockBuilder.toString().contains("responseBody.property2 ==~ java.util.regex.Pattern.compile('[0-9]{3}')") + blockBuilder.toString().contains("\$[?(@.property2 =~ /[0-9]{3}/)]") + blockBuilder.toString().contains("\$[?(@.property1 == 'a')]") } def "should generate regex assertions for string objects in response body"() { @@ -238,14 +236,13 @@ class MockMvcSpockMethodBuilderSpec extends Specification { } } - SpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) then: - blockBuilder.toString().contains("responseBody.property1 == '''a'''") - blockBuilder.toString().contains("responseBody.property2 ==~ java.util.regex.Pattern.compile('[0-9]{3}')") - + blockBuilder.toString().contains("\$[?(@.property2 =~ /[0-9]{3}/)]") + blockBuilder.toString().contains("\$[?(@.property1 == 'a')]") } def "should generate a call with an url path and query parameters"() { @@ -278,15 +275,15 @@ class MockMvcSpockMethodBuilderSpec extends Specification { """ } } - SpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) def spockTest = blockBuilder.toString() then: spockTest.contains('get("/users?limit=10&offset=20&filter=email&sort=name&search=55&age=99&name=Denis.Stepanov&email=bob@email.com")') - spockTest.contains("responseBody.property1 == '''a'''") - spockTest.contains("responseBody.property2 == '''b'''") + spockTest.contains('$[?(@.property2 == \'b\')]') + spockTest.contains('$[?(@.property1 == \'a\')]') } def "should generate test for empty body"() { @@ -301,7 +298,7 @@ class MockMvcSpockMethodBuilderSpec extends Specification { status 406 } } - SpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) @@ -322,45 +319,14 @@ class MockMvcSpockMethodBuilderSpec extends Specification { body "test" } } - SpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) def spockTest = blockBuilder.toString() then: spockTest.contains('def responseBody = (response.body.asString())') - spockTest.contains("responseBody == '''test'''") - } - - @Issue("#127") - def 'should use "stub" as an alias for "client"'() { - given: - GroovyDsl contractDsl = GroovyDsl.make { - request { - method "GET" - url "test" - } - response { - status 200 - body( - property: value( - stub(123), - test(123) - ) - ) - headers { - header('Content-Type': 'application/json') - - } - - } - } - MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) - BlockBuilder blockBuilder = new BlockBuilder(" ") - when: - builder.appendTo(blockBuilder) - then: - blockBuilder.toString().contains("responseBody.property == 123") + spockTest.contains('responseBody == "test"') } @Issue('113') @@ -387,7 +353,7 @@ class MockMvcSpockMethodBuilderSpec extends Specification { } } } - SpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) @@ -420,7 +386,7 @@ class MockMvcSpockMethodBuilderSpec extends Specification { } } } - SpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) @@ -429,31 +395,38 @@ class MockMvcSpockMethodBuilderSpec extends Specification { spockTest.contains('''response.header('Location') ==~ java.util.regex.Pattern.compile('^((http[s]?|ftp):\\/)\\/?([^:\\/\\s]+)(:[0-9]{1,5})?/partners/[0-9]+/users/[0-9]+')''') } - @Issue('124') - def "should create proper response body matching for multiline strings"() { + def "should work with more complex stuff and jsonpaths"() { given: GroovyDsl contractDsl = GroovyDsl.make { - + priority 10 request { method 'POST' - url '/invitations' + url '/validation/client' + headers { + header 'Content-Type': 'application/json' + } + body( + bank_account_number: '0014282912345698765432161182', + email: 'foo@bar.com', + phone_number: '100299300', + personal_id: 'ABC123456' + ) } response { - status 422 - body( - message: "First line\n" + - " Second line" - ) + status 200 + body(errors: [ + [property: "bank_account_number", message: "incorrect_format"] + ]) } } - SpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) def spockTest = blockBuilder.toString() then: - spockTest.contains("""responseBody.message == '''First line - Second line'''""") + spockTest.contains('''$.errors[*][?(@.property == 'bank_account_number')]''') + spockTest.contains('''$.errors[*][?(@.message == 'incorrect_format')]''') } } diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslResponseSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslResponseSpec.groovy deleted file mode 100644 index 07eb9aebda..0000000000 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslResponseSpec.groovy +++ /dev/null @@ -1,52 +0,0 @@ -package io.codearte.accurest.dsl - -import groovy.json.JsonSlurper -import spock.lang.Specification - -class WireMockGroovyDslResponseSpec extends Specification { - - def 'should generate response without body for client side'() { - given: - GroovyDsl dsl = GroovyDsl.make { - response { - status 200 - } - } - expect: - new WireMockResponseStubStrategy(dsl).buildClientResponseContent() == new JsonSlurper().parseText(expectedStub) - where: - expectedStub << [''' - { - "status": 200 - } - ''', - - ''' - { - "status": 200 - } - '''] - } - - def 'should generate headers for response for client side'() { - given: - GroovyDsl dsl = GroovyDsl.make { - response { - headers { - header 'Content-Type', $(client('text/xml'), server('text/*')) - } - status 200 - } - } - expect: - new WireMockResponseStubStrategy(dsl).buildClientResponseContent() == new JsonSlurper().parseText(''' - { - "headers": { - "Content-Type": "text/xml" - }, - "status": 200 - } - ''') - } - -} diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy index cb0a75255a..3356c1a7c9 100755 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy @@ -1,7 +1,7 @@ package io.codearte.accurest.dsl - import groovy.json.JsonBuilder import groovy.json.JsonSlurper +import org.skyscreamer.jsonassert.JSONAssert import spock.lang.Issue class WireMockGroovyDslSpec extends WireMockSpec { @@ -35,21 +35,21 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() then: - new JsonSlurper().parseText(wireMockStub) == new JsonSlurper().parseText(''' -{ - "request": { - "method": "GET", - "urlPattern": "/[0-9]{2}" - }, - "response": { - "status": 200, - "body": "{\\"id\\":\\"123\\",\\"surname\\":\\"Kowalsky\\",\\"name\\":\\"Jan\\",\\"created\\":\\"2014-02-02 12:23:43\\"}", - "headers": { - "Content-Type": "text/plain" - } - } -} -''') + JSONAssert.assertEquals(''' + { + "request" : { + "urlPattern" : "/[0-9]{2}", + "method" : "GET" + }, + "response" : { + "status" : 200, + "body" : "{\\"id\\":\\"123\\",\\"surname\\":\\"Kowalsky\\",\\"name\\":\\"Jan\\",\\"created\\":\\"2014-02-02 12:23:43\\"}", + "headers" : { + "Content-Type" : "text/plain" + } + } + } + ''', wireMockStub, false) and: stubMappingIsValidWireMockStub(wireMockStub) } @@ -80,23 +80,23 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() then: - new JsonSlurper().parseText(wireMockStub) == new JsonSlurper().parseText(''' + JSONAssert.assertEquals(''' { - "request": { - "method": "GET", - "headers": { - "Content-Type": { - "equalTo": "application/vnd.pl.devoxx.aggregatr.v1+json" - } - }, - "url": "/ingredients" - }, - "response": { - "status": 200, - "body": "{\\"ingredients\\":[{\\"type\\":\\"MALT\\",\\"quantity\\":100},{\\"type\\":\\"WATER\\",\\"quantity\\":200},{\\"type\\":\\"HOP\\",\\"quantity\\":300},{\\"type\\":\\"YIEST\\",\\"quantity\\":400}]}" + "request" : { + "url" : "/ingredients", + "method" : "GET", + "headers" : { + "Content-Type" : { + "equalTo" : "application/vnd.pl.devoxx.aggregatr.v1+json" + } } + }, + "response" : { + "status" : 200, + "body" : "{\\"ingredients\\":[{\\"type\\":\\"MALT\\",\\"quantity\\":100},{\\"type\\":\\"WATER\\",\\"quantity\\":200},{\\"type\\":\\"HOP\\",\\"quantity\\":300},{\\"type\\":\\"YIEST\\",\\"quantity\\":400}]}" + } } -''') +''', wireMockStub, false) and: stubMappingIsValidWireMockStub(wireMockStub) } @@ -128,7 +128,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() then: - new JsonSlurper().parseText(wireMockStub) == new JsonSlurper().parseText(''' + JSONAssert.assertEquals(''' { "request": { "method": "POST", @@ -149,7 +149,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { "body": "{\\"paymentId\\":\\"4\\",\\"foundExistingPayment\\":false}" } } -''') +''', wireMockStub, false) and: stubMappingIsValidWireMockStub(wireMockStub) } @@ -180,21 +180,21 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() then: - new JsonSlurper().parseText(wireMockStub) == new JsonSlurper().parseText(''' + JSONAssert.assertEquals((''' { - "request": { - "method": "GET", - "urlPattern": "/[0-9]{2}" - }, - "response": { - "status": 200, - "body": "{\\"created\\":\\"2014-02-02 12:23:43\\",\\"id\\":\\"123\\",\\"name\\":\\"Jan\\",\\"surname\\":\\"Kowalsky\\"}", - "headers": { - "Content-Type": "text/plain" - } + "request" : { + "urlPattern" : "/[0-9]{2}", + "method" : "GET" + }, + "response" : { + "status" : 200, + "body" : "{\\"created\\":\\"2014-02-02 12:23:43\\",\\"id\\":\\"123\\",\\"name\\":\\"Jan\\",\\"surname\\":\\"Kowalsky\\"}", + "headers" : { + "Content-Type" : "text/plain" } + } } -''') +'''), wireMockStub, false) and: stubMappingIsValidWireMockStub(wireMockStub) } @@ -227,26 +227,24 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() then: - new JsonSlurper().parseText(wireMockStub) == new JsonSlurper().parseText(''' + JSONAssert.assertEquals(''' { - "request": { - "method": "GET", - "urlPattern": "/[0-9]{2}", - "bodyPatterns": [ - { - "equalToJson":"{\\"name\\":\\"Jan\\"}" - } - ] - }, - "response": { - "status": 200, - "body": "{\\"name\\":\\"Jan\\"}", - "headers": { - "Content-Type": "text/plain" - } + "request" : { + "urlPattern" : "/[0-9]{2}", + "method" : "GET", + "bodyPatterns" : [ { + "matchesJsonPath" : "$[?(@.name == 'Jan')]" + } ] + }, + "response" : { + "status" : 200, + "body" : "{\\"name\\":\\"Jan\\"}", + "headers" : { + "Content-Type" : "text/plain" } + } } -''') +''', wireMockStub, false) and: stubMappingIsValidWireMockStub(wireMockStub) } @@ -277,22 +275,26 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() then: - new JsonSlurper().parseText(wireMockStub) == new JsonSlurper().parseText(''' - { - "request": { - "method": "GET", - "urlPattern": "/[0-9]{2}", - "bodyPatterns": [ - { - "equalToJson": "{\\"id\\":\\"123\\",\\"surname\\":\\"Kowalsky\\",\\"name\\":\\"Jan\\",\\"created\\":\\"2014-02-02 12:23:43\\"}" - } - ] - }, - "response": { - "status": 200, - } - } - ''') + JSONAssert.assertEquals((''' +{ + "request" : { + "urlPattern" : "/[0-9]{2}", + "method" : "GET", + "bodyPatterns" : [ { + "matchesJsonPath" : "$[?(@.created == '2014-02-02 12:23:43')]" + }, { + "matchesJsonPath" : "$[?(@.surname == 'Kowalsky')]" + }, { + "matchesJsonPath" : "$[?(@.name == 'Jan')]" + }, { + "matchesJsonPath" : "$[?(@.id == '123')]" + } ] + }, + "response" : { + "status" : 200 + } +} + '''), wireMockStub, false) and: stubMappingIsValidWireMockStub(wireMockStub) } @@ -319,27 +321,25 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: String json = toWireMockClientJsonStub(groovyDsl) then: - parseJson(json) == parseJson(''' - { - "request": { - "method": "GET", - "url": "/users", - "headers": { - "Content-Type": { - "equalTo": "customtype/json" - } - }, - "bodyPatterns": [ - { - "equalToJson":"{\\"name\\":\\"Jan\\"}" - } - ] - }, - "response": { - "status": 200 - } - } - ''') + JSONAssert.assertEquals((''' +{ + "request" : { + "url" : "/users", + "method" : "GET", + "bodyPatterns" : [ { + "matchesJsonPath" : "$[?(@.name == 'Jan')]" + } ], + "headers" : { + "Content-Type" : { + "equalTo" : "customtype/json" + } + } + }, + "response" : { + "status" : 200 + } +} + '''), json, false) and: stubMappingIsValidWireMockStub(json) } @@ -364,7 +364,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: String json = toWireMockClientJsonStub(groovyDsl) then: - parseJson(json) == parseJson(''' + JSONAssert.assertEquals((''' { "request": { "method": "GET", @@ -384,7 +384,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { "status": 200 } } - ''') + '''), json, false) and: stubMappingIsValidWireMockStub(json) } @@ -406,7 +406,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: String json = toWireMockClientJsonStub(groovyDsl) then: - parseJson(json) == parseJson(''' + JSONAssert.assertEquals((''' { "request": { "method": "GET", @@ -421,7 +421,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { "status": 200 } } - ''') + '''), json, false) and: stubMappingIsValidWireMockStub(json) } @@ -443,7 +443,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: String json = toWireMockClientJsonStub(groovyDsl) then: - parseJson(json) == parseJson(''' + JSONAssert.assertEquals((''' { "request": { "method": "GET", @@ -454,7 +454,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { "body":"Jozo<test>" } } - ''') + '''), json, false) and: stubMappingIsValidWireMockStub(json) } @@ -474,7 +474,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: String json = toWireMockClientJsonStub(groovyDsl) then: - parseJson(json) == parseJson(''' + JSONAssert.assertEquals((''' { "request": { "method": "GET", @@ -489,7 +489,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { "status": 200 } } - ''') + '''), json, false) and: stubMappingIsValidWireMockStub(json) } @@ -511,7 +511,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: String json = toWireMockClientJsonStub(groovyDsl) then: - parseJson(json) == parseJson(''' + JSONAssert.assertEquals((''' { "request": { "method": "GET", @@ -526,7 +526,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { "status": 200 } } - ''') + '''), json, false) and: stubMappingIsValidWireMockStub(json) } @@ -559,24 +559,24 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() then: - new JsonSlurper().parseText(wireMockStub) == new JsonSlurper().parseText(''' + JSONAssert.assertEquals((''' { - "request": { - "method": "GET", - "urlPattern": "/[0-9]{2}", - "bodyPatterns": [ - {"matches": "\\\\s*\\\\{\\\\s*\\\"personalId\\\"\\\\s*:\\\\s*\\\"?^[0-9]{11}$\\\"?\\\\s*\\\\}\\\\s*"} - ] - }, - "response": { - "status": 200, - "body": "{\\"name\\":\\"Jan\\"}", - "headers": { - "Content-Type": "text/plain" - } + "request" : { + "urlPattern" : "/[0-9]{2}", + "method" : "GET", + "bodyPatterns" : [ { + "matchesJsonPath" : "$[?(@.personalId =~ /^[0-9]{11}$/)]" + } ] + }, + "response" : { + "status" : 200, + "body" : "{\\"name\\":\\"Jan\\"}", + "headers" : { + "Content-Type" : "text/plain" } + } } -''') +'''), wireMockStub, false) and: stubMappingIsValidWireMockStub(wireMockStub) } @@ -614,84 +614,35 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() then: - new JsonSlurper().parseText(wireMockStub) == new JsonSlurper().parseText(''' + JSONAssert.assertEquals((''' { - "request": { - "method": "PUT", - "headers": { - "Content-Type": { - "equalTo": "application/vnd.fraud.v1+json" - } - }, - "url": "/fraudcheck", - "bodyPatterns": [ - {"matches": "\\\\s*\\\\{\\\\s*\\"clientPesel\\"\\\\s*:\\\\s*\\"?[0-9]{10}\\"?\\\\s*,\\\\s*\\"loanAmount\\"\\\\s*:\\\\s*\\"?123.123\\"?\\\\s*\\\\}\\\\s*"} - ] - }, - "response": { - "status": 200, - "headers": { - "Content-Type": "application/vnd.fraud.v1+json" - }, - "body": "{\\"fraudCheckStatus\\":\\"OK\\",\\"rejectionReason\\":null}" + "request" : { + "url" : "/fraudcheck", + "method" : "PUT", + "bodyPatterns" : [ { + "matchesJsonPath" : "$[?(@.loanAmount == 123.123)]" + }, { + "matchesJsonPath" : "$[?(@.clientPesel =~ /[0-9]{10}/)]" + } ], + "headers" : { + "Content-Type" : { + "equalTo" : "application/vnd.fraud.v1+json" + } } + }, + "response" : { + "status" : 200, + "body" : "{\\"fraudCheckStatus\\":\\"OK\\",\\"rejectionReason\\":null}", + "headers" : { + "Content-Type" : "application/vnd.fraud.v1+json" + } + } } -''') +'''), wireMockStub, false) and: stubMappingIsValidWireMockStub(wireMockStub) } - - def "should generate stub with GET"() { - given: - GroovyDsl groovyDsl = GroovyDsl.make { - request { - method("GET") - } - } - expect: - new WireMockRequestStubStrategy(groovyDsl).buildClientRequestContent() == new JsonSlurper().parseText(''' - { - "method":"GET" - } - ''') - } - - def "should generate request when two elements are provided "() { - given: - GroovyDsl groovyDsl = GroovyDsl.make { - request { - method("GET") - url("/sth") - } - } - expect: - new WireMockRequestStubStrategy(groovyDsl).buildClientRequestContent() == new JsonSlurper().parseText(''' - { - "method":"GET", - "url":"/sth" - } - ''') - } - - def "should generate request with urlPattern for client side"() { - given: - GroovyDsl groovyDsl = GroovyDsl.make { - request { - url $( - client(~/^\/[0-9]{2}$/), - server('/12') - ) - } - } - expect: - new WireMockRequestStubStrategy(groovyDsl).buildClientRequestContent() == new JsonSlurper().parseText(''' - { - "urlPattern":"^/[0-9]{2}$" - } - ''') - } - def "should generate request with urlPath and queryParameters for client side"() { given: GroovyDsl groovyDsl = GroovyDsl.make { @@ -717,7 +668,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: def json = toWireMockClientJsonStub(groovyDsl) then: - parseJson(json) == parseJson(''' + JSONAssert.assertEquals((''' { "request": { "method": "GET", @@ -753,7 +704,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { "status": 200, } } - ''') + '''), json, false) and: stubMappingIsValidWireMockStub(json) } @@ -772,7 +723,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: def json = toWireMockClientJsonStub(groovyDsl) then: - parseJson(json) == parseJson(''' + JSONAssert.assertEquals((''' { "request": { "method": "GET", @@ -782,7 +733,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { "status": 200, } } - ''') + '''), json, false) and: stubMappingIsValidWireMockStub(json) } @@ -801,7 +752,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: def json = toWireMockClientJsonStub(groovyDsl) then: - parseJson(json) == parseJson(''' + JSONAssert.assertEquals((''' { "request": { "method": "GET", @@ -811,7 +762,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { "status": 200, } } - ''') + '''), json, false) and: stubMappingIsValidWireMockStub(json) } @@ -947,7 +898,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: def json = toWireMockClientJsonStub(groovyDsl) then: - parseJson(json) == parseJson(''' + JSONAssert.assertEquals((''' { "request": { "method": "GET", @@ -965,46 +916,11 @@ class WireMockGroovyDslSpec extends WireMockSpec { "status": 200, } } - ''') + '''), json, false) and: stubMappingIsValidWireMockStub(json) } - def "should generate stub with some headers section for client side"() { - given: - GroovyDsl groovyDsl = GroovyDsl.make { - request { - headers { - header('Content-Type': 'text/xml') - header('Accept': $( - client(regex('text/.*')), - server('text/plain') - )) - header('X-Custom-Header': $( - client(regex('^.*2134.*$')), - server('121345') - )) - } - } - } - expect: - new WireMockRequestStubStrategy(groovyDsl).buildClientRequestContent() == new JsonSlurper().parseText(''' - { - "headers": { - "Content-Type": { - "equalTo": "text/xml" - }, - "Accept": { - "matches": "text/.*" - }, - "X-Custom-Header": { - "matches": "^.*2134.*$" - } - } - } - ''') - } - def 'should convert groovy dsl stub with rich tree Body as String to wireMock stub for the client side'() { given: GroovyDsl groovyDsl = GroovyDsl.make { @@ -1046,25 +962,38 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() then: - new JsonSlurper().parseText(wireMockStub) == new JsonSlurper().parseText(''' - { - "request": { - "method": "GET", - "urlPattern": "/[0-9]{2}", - "bodyPatterns": [ - { - "matches": "\\\\s*\\\\{\\\\s*\\"birthDate\\"\\\\s*:\\\\s*\\"?[0-9]{4}-[0-9]{2}-[0-9]{2}\\"?\\\\s*,\\\\s*\\"errors\\"\\\\s*:\\\\s*\\\\[\\\\s*\\\\{\\\\s*\\"propertyName\\"\\\\s*:\\\\s*\\"?[0-9]{2}\\"?\\\\s*,\\\\s*\\"providerValue\\"\\\\s*:\\\\s*\\"?Test\\"?\\\\s*\\\\}\\\\s*,\\\\s*\\\\{\\\\s*\\"propertyName\\"\\\\s*:\\\\s*\\"?[0-9]{2}\\"?\\\\s*,\\\\s*\\"providerValue\\"\\\\s*:\\\\s*\\"?Test\\"?\\\\s*\\\\}\\\\s*\\\\]\\\\s*,\\\\s*\\"firstName\\"\\\\s*:\\\\s*\\"?.*\\"?\\\\s*,\\\\s*\\"lastName\\"\\\\s*:\\\\s*\\"?.*\\"?\\\\s*,\\\\s*\\"personalId\\"\\\\s*:\\\\s*\\"?[0-9]{11}\\"?\\\\s*\\\\}\\\\s*" - } - ] }, - "response": { - "status": 200, - "body": "{\\"name\\":\\"Jan\\"}", - "headers": { - "Content-Type": "text/plain" - } - } - } - ''') + JSONAssert.assertEquals((''' +{ + "request" : { + "urlPattern" : "/[0-9]{2}", + "method" : "GET", + "bodyPatterns" : [ { + "matchesJsonPath" : "$.errors[*][?(@.propertyName =~ /[0-9]{2}/)]" + }, { + "matchesJsonPath" : "$.errors[*][?(@.providerValue == 'Test')]" + }, { + "matchesJsonPath" : "$.errors[*][?(@.providerValue == 'Test')]" + }, { + "matchesJsonPath" : "$[?(@.lastName =~ /.*/)]" + }, { + "matchesJsonPath" : "$.errors[*][?(@.propertyName =~ /[0-9]{2}/)]" + }, { + "matchesJsonPath" : "$[?(@.birthDate =~ /[0-9]{4}-[0-9]{2}-[0-9]{2}/)]" + }, { + "matchesJsonPath" : "$[?(@.personalId =~ /[0-9]{11}/)]" + }, { + "matchesJsonPath" : "$[?(@.firstName =~ /.*/)]" + } ] + }, + "response" : { + "status" : 200, + "body" : "{\\"name\\":\\"Jan\\"}", + "headers" : { + "Content-Type" : "text/plain" + } + } +} + '''), wireMockStub, false) } def 'should use regexp matches when request body match is defined using a map with a pattern'() { @@ -1097,26 +1026,34 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: def json = toWireMockClientJsonStub(groovyDsl) then: - parseJson(json) == parseJson(''' - { - "request": { - "method": "POST", - "url": "/reissue-payment-order", - "bodyPatterns": [ - { - "matches": "\\\\s*\\\\{\\\\s*\\"loanNumber\\"\\\\s*:\\\\s*\\"?999997001\\"?\\\\s*,\\\\s*\\"amount\\"\\\\s*:\\\\s*\\"?[0-9.]+\\"?\\\\s*,\\\\s*\\"currency\\"\\\\s*:\\\\s*\\"?DKK\\"?\\\\s*,\\\\s*\\"applicationName\\"\\\\s*:\\\\s*\\"?.*\\"?\\\\s*,\\\\s*\\"username\\"\\\\s*:\\\\s*\\"?.*\\"?\\\\s*,\\\\s*\\"cardId\\"\\\\s*:\\\\s*\\"?1\\"?\\\\s*\\\\}\\\\s*" - } - ] - }, - "response": { - "status": 200, - "body": "{\\"status\\":\\"OK\\"}", - "headers": { - "Content-Type": "application/json" - } - } - } - ''') + JSONAssert.assertEquals((''' +{ + "request" : { + "url" : "/reissue-payment-order", + "method" : "POST", + "bodyPatterns" : [ { + "matchesJsonPath" : "$[?(@.loanNumber == '999997001')]" + }, { + "matchesJsonPath" : "$[?(@.username =~ /.*/)]" + }, { + "matchesJsonPath" : "$[?(@.amount =~ /[0-9.]+/)]" + }, { + "matchesJsonPath" : "$[?(@.cardId == 1)]" + }, { + "matchesJsonPath" : "$[?(@.currency == 'DKK')]" + }, { + "matchesJsonPath" : "$[?(@.applicationName =~ /.*/)]" + } ] + }, + "response" : { + "status" : 200, + "body" : "{\\"status\\":\\"OK\\"}", + "headers" : { + "Content-Type" : "application/json" + } + } +} + '''), json, false) } def "should generate stub for empty body"() { @@ -1134,7 +1071,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: def json = toWireMockClientJsonStub(groovyDsl) then: - parseJson(json) == parseJson(''' + JSONAssert.assertEquals((''' { "request": { "method": "POST", @@ -1149,7 +1086,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { "status": 406 } } -''') +'''), json, false) } def "should generate stub with priority"() { @@ -1167,7 +1104,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: def json = toWireMockClientJsonStub(groovyDsl) then: - parseJson(json) == parseJson(''' + JSONAssert.assertEquals((''' { "priority": 9, "request": { @@ -1178,7 +1115,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { "status": 406 } } - ''') + '''), json, false) } @Issue("#127") @@ -1198,21 +1135,19 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() then: - new JsonSlurper().parseText(wireMockStub) == new JsonSlurper().parseText(''' - { - "request": { - "method": "POST", - "bodyPatterns": [ - { - "equalToJson": "{\\"property\\":\\"value\\"}" - } - ] - }, - "response": { - "status": 200 - } - } - ''') + JSONAssert.assertEquals((''' +{ + "request" : { + "method" : "POST", + "bodyPatterns" : [ { + "matchesJsonPath" : "$[?(@.property == 'value')]" + } ] + }, + "response" : { + "status" : 200 + } +} + '''), wireMockStub, false) and: stubMappingIsValidWireMockStub(wireMockStub) } @@ -1234,7 +1169,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() then: - new JsonSlurper().parseText(wireMockStub) == new JsonSlurper().parseText(''' + JSONAssert.assertEquals((''' { "request": { "method": "POST", @@ -1248,7 +1183,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { "status": 200 } } - ''') + '''), wireMockStub, false) and: stubMappingIsValidWireMockStub(wireMockStub) } diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/util/JsonPathJsonConverterSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/util/JsonPathJsonConverterSpec.groovy new file mode 100644 index 0000000000..a7e9f621d7 --- /dev/null +++ b/accurest-core/src/test/groovy/io/codearte/accurest/util/JsonPathJsonConverterSpec.groovy @@ -0,0 +1,199 @@ +package io.codearte.accurest.util +import groovy.json.JsonOutput +import groovy.json.JsonSlurper +import com.jayway.jsonpath.Configuration +import com.jayway.jsonpath.DocumentContext +import com.jayway.jsonpath.JsonPath +import com.jayway.jsonpath.Option +import net.minidev.json.JSONArray +import spock.lang.Specification +import spock.lang.Unroll + +import java.util.regex.Pattern + +class JsonPathJsonConverterSpec extends Specification { + + @Unroll + def 'should convert a json with list as root to a map of path to value'() { + when: + JsonPaths pathAndValues = JsonPathJsonConverter.transformToJsonPathWithTestsSideValues(new JsonSlurper().parseText(json)) + then: + pathAndValues['''$[*].some.nested[?(@.json == 'with value')]'''] == 'with value' + pathAndValues['''$[*].some.nested[?(@.anothervalue == 4)]'''] == 4 + pathAndValues['''$[*].some.nested.withlist[*][?(@.name == 'name1')]'''] == 'name1' + pathAndValues['''$[*].some.nested.withlist[*][?(@.name == 'name2')]'''] == 'name2' + pathAndValues['''$[*].some.nested.withlist[*].anothernested[?(@.name == 'name3')]'''] == 'name3' + and: + assertThatJsonPathsInMapAreValid(json, pathAndValues) + where: + json << [ + ''' + [ { + "some" : { + "nested" : { + "json" : "with value", + "anothervalue": 4, + "withlist" : [ + { "name" :"name1"} , {"name": "name2"}, {"anothernested": { "name": "name3"} } + ] + } + } + }, + { + "someother" : { + "nested" : { + "json" : "with value", + "anothervalue": 4, + "withlist" : [ + { "name" :"name1"} , {"name": "name2"} + ] + } + } + } + ] + ''', + ''' + [{ + "someother" : { + "nested" : { + "json" : "with value", + "anothervalue": 4, + "withlist" : [ + { "name" :"name1"} , {"name": "name2"} + ] + } + } + }, + { + "some" : { + "nested" : { + "json" : "with value", + "anothervalue": 4, + "withlist" : [ + {"name": "name2"}, {"anothernested": { "name": "name3"} }, { "name" :"name1"} + ] + } + } + } + ]'''] + } + + def 'should convert a json with a map as root to a map of path to value'() { + given: + String json = ''' + { + "some" : { + "nested" : { + "json" : "with value", + "anothervalue": 4, + "withlist" : [ + { "name" :"name1"} , {"name": "name2"} + ] + } + } + } +''' + when: + JsonPaths pathAndValues = JsonPathJsonConverter.transformToJsonPathWithTestsSideValues(new JsonSlurper().parseText(json)) + then: + pathAndValues['''$.some.nested[?(@.json == 'with value')]'''] == 'with value' + pathAndValues['''$.some.nested[?(@.anothervalue == 4)]'''] == 4 + pathAndValues['''$.some.nested.withlist[*][?(@.name == 'name1')]'''] == 'name1' + pathAndValues['''$.some.nested.withlist[*][?(@.name == 'name2')]'''] == 'name2' + and: + assertThatJsonPathsInMapAreValid(json, pathAndValues) + } + + def 'should convert a json with a list'() { + given: + String json = ''' + { + "items" : ["HOP"] + } +''' + when: + JsonPaths pathAndValues = JsonPathJsonConverter.transformToJsonPathWithTestsSideValues(new JsonSlurper().parseText(json)) + then: + pathAndValues['''$.items[?(@ == 'HOP')]'''] == 'HOP' + and: + assertThatJsonPathsInMapAreValid(json, pathAndValues) + } + + + def 'should convert a json with a list of errors'() { + given: + String json = ''' + { + "errors" : [ + { "property" : "email", "message" : "inconsistent value" }, + { "property" : "email", "message" : "inconsistent value2" } + ] + } +''' + when: + JsonPaths pathAndValues = JsonPathJsonConverter.transformToJsonPathWithTestsSideValues(new JsonSlurper().parseText(json)) + then: + pathAndValues['''$.errors[*][?(@.property == 'email')]'''] == 'email' + pathAndValues['''$.errors[*][?(@.message == 'inconsistent value')]'''] == 'inconsistent value' + pathAndValues['''$.errors[*][?(@.message == 'inconsistent value2')]'''] == 'inconsistent value2' + pathAndValues['''$.errors[*][?(@.property == 'email')]'''] == 'email' + and: + assertThatJsonPathsInMapAreValid(json, pathAndValues) + } + + + def 'should convert a map json with a regex pattern'() { + given: + List json = [ + [some: + [nested: [ + json: "with value", + anothervalue: 4, + withlist: + [ + [name: "name2"], + [name: "name1"], + [anothernested: + [name: Pattern.compile('[a-zA-Z]+')] + ], + [age: "123456789"] + ] + ] + ] + ], + [someother: + [nested: [ + json: "with value", + anothervalue: 4, + withlist: + [ + [name: "name2"], + [name: "name1"] + ] + ] + ] + ] + ] + when: + JsonPaths pathAndValues = JsonPathJsonConverter.transformToJsonPathWithTestsSideValues(json) + then: + pathAndValues['''$[*].some.nested[?(@.json == 'with value')]'''] == 'with value' + pathAndValues['''$[*].some.nested[?(@.anothervalue == 4)]'''] == 4 + pathAndValues['''$[*].some.nested.withlist[*][?(@.name == 'name1')]'''] == 'name1' + pathAndValues['''$[*].some.nested.withlist[*][?(@.name == 'name2')]'''] + (pathAndValues['''$[*].some.nested.withlist[*].anothernested[?(@.name =~ /[a-zA-Z]+/)]'''] as Pattern).pattern() == '[a-zA-Z]+' + when: + pathAndValues['''$[*].some.nested.withlist[*].anothernested[?(@.name =~ /[a-zA-Z]+/)]'''] = "Kowalski" + json.some.nested.withlist[0][2].anothernested.name = "Kowalski" + then: + assertThatJsonPathsInMapAreValid(JsonOutput.prettyPrint(JsonOutput.toJson(json)), pathAndValues) + } + + private void assertThatJsonPathsInMapAreValid(String json, JsonPaths pathAndValues) { + DocumentContext parsedJson = JsonPath.using(Configuration.builder().options(Option.ALWAYS_RETURN_LIST).build()).parse(json); + pathAndValues.each { + assert parsedJson.read(it.jsonPath, JSONArray).getAt(it.optionalSuffix ?: 0) == it.optionalSuffix ? [it.value] : it.value + } + } + +} diff --git a/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/AccurestGradlePlugin.groovy b/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/AccurestGradlePlugin.groovy index dcec7b2f61..f93809b40a 100644 --- a/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/AccurestGradlePlugin.groovy +++ b/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/AccurestGradlePlugin.groovy @@ -4,6 +4,7 @@ import io.codearte.accurest.config.AccurestConfigProperties import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.Task +import org.gradle.api.artifacts.DependencyResolveDetails /** * @author Jakub Kubrynski @@ -29,6 +30,21 @@ class AccurestGradlePlugin implements Plugin { createGenerateTestsTask(extension) createAndConfigureGenerateWireMockClientStubsFromDslTask(extension) deprecatedCreateAndConfigureGenerateWiremockClientStubsFromDslTask() + project.dependencies.add("testCompile", "io.codearte.accurest:accurest-core:+") + project.dependencies.add("testCompile", "com.blogspot.toomuchcoding:wiremock:0.0.1") + project.repositories { jcenter() } + project.configurations { + all { + resolutionStrategy { + eachDependency { DependencyResolveDetails details -> + if (details.requested.group == 'com.github.tomakehurst' && details.requested.name == "wiremock") { + details.useTarget("com.blogspot.toomuchcoding:wiremock:0.0.1") + } + } + } + } + } + project.afterEvaluate { def hasIdea = project.plugins.findPlugin(IDEA_PLUGIN_CLASS) diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/build.gradle b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/build.gradle index 068c0f6e47..aa4e17339c 100644 --- a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/build.gradle +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/build.gradle @@ -18,7 +18,6 @@ ext { subprojects { apply plugin: 'groovy' - repositories { mavenCentral() mavenLocal() diff --git a/build.gradle b/build.gradle index 8b8d47bb77..b77eab32f6 100644 --- a/build.gradle +++ b/build.gradle @@ -48,6 +48,7 @@ subprojects { repositories { mavenLocal() mavenCentral() + jcenter() } //Dependencies in all subprojects - http://solidsoft.wordpress.com/2014/11/13/gradle-tricks-display-dependencies-for-all-subprojects-in-multi-project-build/ @@ -84,15 +85,21 @@ subprojects { } project(':accurest-core') { + dependencies { compile 'org.slf4j:slf4j-api:[1.6.0,)' compile 'org.codehaus.plexus:plexus-utils:[3.0.0,)' compile 'commons-io:commons-io:[2.0,)' compile 'org.apache.commons:commons-lang3:[3.3,)' + compile 'com.google.code.gson:gson:2.3.1' + compile 'com.fasterxml.jackson.core:jackson-databind:2.4.5' + compile 'asm:asm:3.3.1' + compile 'com.blogspot.toomuchcoding:wiremock:0.0.1' testCompile 'cglib:cglib-nodep:2.2' testCompile 'org.objenesis:objenesis:2.1' - testCompile 'com.github.tomakehurst:wiremock:1.57' + testCompile 'org.skyscreamer:jsonassert:1.2.3' } + } project(':accurest-converters') { @@ -101,7 +108,7 @@ project(':accurest-converters') { compile 'org.apache.commons:commons-lang3:[3.0,)' compile 'commons-io:commons-io:[2.0,)' compile 'dk.brics.automaton:automaton:1.11-8' // needed for Xeger - testCompile 'com.github.tomakehurst:wiremock:1.57' + testCompile 'com.blogspot.toomuchcoding:wiremock:0.0.1' testCompile 'org.hamcrest:hamcrest-all:1.3' } } diff --git a/gradle/release.gradle b/gradle/release.gradle index 34e8ae08ef..3300caac3c 100644 --- a/gradle/release.gradle +++ b/gradle/release.gradle @@ -12,7 +12,7 @@ task sourcesJar(type: Jar) { } artifacts { - archives javadocJar, sourcesJar + archives javadocJar, sourcesJar, repackagedJar } signing { From 0021a0dd70820cdf2848cd5cc4d2d036f9b92da7 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Sun, 30 Aug 2015 19:33:21 +0200 Subject: [PATCH 075/119] Went towards beta version of wiremock --- .../dsl/WireMockResponseStubStrategy.groovy | 21 +++++++++---------- .../plugin/AccurestGradlePlugin.groovy | 6 +----- build.gradle | 6 ++++-- gradle/release.gradle | 2 +- 4 files changed, 16 insertions(+), 19 deletions(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockResponseStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockResponseStubStrategy.groovy index 5af5898257..3df86b21ef 100755 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockResponseStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockResponseStubStrategy.groovy @@ -25,30 +25,29 @@ class WireMockResponseStubStrategy extends BaseWireMockStubStrategy { @PackageScope ResponseDefinition buildClientResponseContent() { - ResponseDefinition responseDefinition = new ResponseDefinition() - responseDefinition.setStatus(response.status.clientValue as Integer) - appendHeaders(responseDefinition) - appendBody(responseDefinition) - return responseDefinition + int status = response.status.clientValue as Integer + HttpHeaders httpHeaders = getHttpHeaders() + String body = getBody() + return new ResponseDefinition(status, body as String, null, null, null, httpHeaders, null, null, null, null, null) } - private void appendHeaders(ResponseDefinition responseDefinition) { + private HttpHeaders getHttpHeaders() { if(!(response.headers)) { - return + return null } - responseDefinition.setHeaders(new HttpHeaders(response.headers.entries?.collect { new HttpHeader(it.name, it.clientValue.toString()) })) + return new HttpHeaders(response.headers.entries?.collect { new HttpHeader(it.name, it.clientValue.toString()) }) } - private void appendBody(ResponseDefinition responseDefinition) { + private String getBody() { if (!response.body) { - return + return null } Object body = response.body.clientValue ContentType contentType = recognizeContentTypeFromHeader(response.headers) if (contentType == ContentType.UNKNOWN) { contentType = recognizeContentTypeFromContent(body) } - responseDefinition.setBody(parseBody(body, contentType)) + return parseBody(body, contentType) } diff --git a/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/AccurestGradlePlugin.groovy b/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/AccurestGradlePlugin.groovy index f93809b40a..1e905a9ff5 100644 --- a/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/AccurestGradlePlugin.groovy +++ b/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/AccurestGradlePlugin.groovy @@ -1,5 +1,4 @@ package io.codearte.accurest.plugin - import io.codearte.accurest.config.AccurestConfigProperties import org.gradle.api.Plugin import org.gradle.api.Project @@ -30,11 +29,9 @@ class AccurestGradlePlugin implements Plugin { createGenerateTestsTask(extension) createAndConfigureGenerateWireMockClientStubsFromDslTask(extension) deprecatedCreateAndConfigureGenerateWiremockClientStubsFromDslTask() - project.dependencies.add("testCompile", "io.codearte.accurest:accurest-core:+") - project.dependencies.add("testCompile", "com.blogspot.toomuchcoding:wiremock:0.0.1") - project.repositories { jcenter() } project.configurations { all { + exclude group: 'org.glassfish.jersey.jetty', module: 'jetty-server' resolutionStrategy { eachDependency { DependencyResolveDetails details -> if (details.requested.group == 'com.github.tomakehurst' && details.requested.name == "wiremock") { @@ -45,7 +42,6 @@ class AccurestGradlePlugin implements Plugin { } } - project.afterEvaluate { def hasIdea = project.plugins.findPlugin(IDEA_PLUGIN_CLASS) if (hasIdea) { diff --git a/build.gradle b/build.gradle index b77eab32f6..2ff7dc03cf 100644 --- a/build.gradle +++ b/build.gradle @@ -94,7 +94,9 @@ project(':accurest-core') { compile 'com.google.code.gson:gson:2.3.1' compile 'com.fasterxml.jackson.core:jackson-databind:2.4.5' compile 'asm:asm:3.3.1' - compile 'com.blogspot.toomuchcoding:wiremock:0.0.1' + compile('com.github.tomakehurst:wiremock:2.0.0-beta') { + exclude group: 'org.mortbay.jetty', module: 'servlet-api' + } testCompile 'cglib:cglib-nodep:2.2' testCompile 'org.objenesis:objenesis:2.1' testCompile 'org.skyscreamer:jsonassert:1.2.3' @@ -108,7 +110,7 @@ project(':accurest-converters') { compile 'org.apache.commons:commons-lang3:[3.0,)' compile 'commons-io:commons-io:[2.0,)' compile 'dk.brics.automaton:automaton:1.11-8' // needed for Xeger - testCompile 'com.blogspot.toomuchcoding:wiremock:0.0.1' + testCompile 'com.github.tomakehurst:wiremock:2.0.0-beta' testCompile 'org.hamcrest:hamcrest-all:1.3' } } diff --git a/gradle/release.gradle b/gradle/release.gradle index 3300caac3c..34e8ae08ef 100644 --- a/gradle/release.gradle +++ b/gradle/release.gradle @@ -12,7 +12,7 @@ task sourcesJar(type: Jar) { } artifacts { - archives javadocJar, sourcesJar, repackagedJar + archives javadocJar, sourcesJar } signing { From ca2c051ea22126a9fe31314212641773961aa56f Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Sun, 30 Aug 2015 20:44:44 +0200 Subject: [PATCH 076/119] Revert "Went towards beta version of wiremock" This reverts commit 0021a0dd70820cdf2848cd5cc4d2d036f9b92da7. --- .../dsl/WireMockResponseStubStrategy.groovy | 21 ++++++++++--------- .../plugin/AccurestGradlePlugin.groovy | 6 +++++- build.gradle | 6 ++---- gradle/release.gradle | 2 +- 4 files changed, 19 insertions(+), 16 deletions(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockResponseStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockResponseStubStrategy.groovy index 3df86b21ef..5af5898257 100755 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockResponseStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockResponseStubStrategy.groovy @@ -25,29 +25,30 @@ class WireMockResponseStubStrategy extends BaseWireMockStubStrategy { @PackageScope ResponseDefinition buildClientResponseContent() { - int status = response.status.clientValue as Integer - HttpHeaders httpHeaders = getHttpHeaders() - String body = getBody() - return new ResponseDefinition(status, body as String, null, null, null, httpHeaders, null, null, null, null, null) + ResponseDefinition responseDefinition = new ResponseDefinition() + responseDefinition.setStatus(response.status.clientValue as Integer) + appendHeaders(responseDefinition) + appendBody(responseDefinition) + return responseDefinition } - private HttpHeaders getHttpHeaders() { + private void appendHeaders(ResponseDefinition responseDefinition) { if(!(response.headers)) { - return null + return } - return new HttpHeaders(response.headers.entries?.collect { new HttpHeader(it.name, it.clientValue.toString()) }) + responseDefinition.setHeaders(new HttpHeaders(response.headers.entries?.collect { new HttpHeader(it.name, it.clientValue.toString()) })) } - private String getBody() { + private void appendBody(ResponseDefinition responseDefinition) { if (!response.body) { - return null + return } Object body = response.body.clientValue ContentType contentType = recognizeContentTypeFromHeader(response.headers) if (contentType == ContentType.UNKNOWN) { contentType = recognizeContentTypeFromContent(body) } - return parseBody(body, contentType) + responseDefinition.setBody(parseBody(body, contentType)) } diff --git a/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/AccurestGradlePlugin.groovy b/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/AccurestGradlePlugin.groovy index 1e905a9ff5..f93809b40a 100644 --- a/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/AccurestGradlePlugin.groovy +++ b/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/AccurestGradlePlugin.groovy @@ -1,4 +1,5 @@ package io.codearte.accurest.plugin + import io.codearte.accurest.config.AccurestConfigProperties import org.gradle.api.Plugin import org.gradle.api.Project @@ -29,9 +30,11 @@ class AccurestGradlePlugin implements Plugin { createGenerateTestsTask(extension) createAndConfigureGenerateWireMockClientStubsFromDslTask(extension) deprecatedCreateAndConfigureGenerateWiremockClientStubsFromDslTask() + project.dependencies.add("testCompile", "io.codearte.accurest:accurest-core:+") + project.dependencies.add("testCompile", "com.blogspot.toomuchcoding:wiremock:0.0.1") + project.repositories { jcenter() } project.configurations { all { - exclude group: 'org.glassfish.jersey.jetty', module: 'jetty-server' resolutionStrategy { eachDependency { DependencyResolveDetails details -> if (details.requested.group == 'com.github.tomakehurst' && details.requested.name == "wiremock") { @@ -42,6 +45,7 @@ class AccurestGradlePlugin implements Plugin { } } + project.afterEvaluate { def hasIdea = project.plugins.findPlugin(IDEA_PLUGIN_CLASS) if (hasIdea) { diff --git a/build.gradle b/build.gradle index 2ff7dc03cf..b77eab32f6 100644 --- a/build.gradle +++ b/build.gradle @@ -94,9 +94,7 @@ project(':accurest-core') { compile 'com.google.code.gson:gson:2.3.1' compile 'com.fasterxml.jackson.core:jackson-databind:2.4.5' compile 'asm:asm:3.3.1' - compile('com.github.tomakehurst:wiremock:2.0.0-beta') { - exclude group: 'org.mortbay.jetty', module: 'servlet-api' - } + compile 'com.blogspot.toomuchcoding:wiremock:0.0.1' testCompile 'cglib:cglib-nodep:2.2' testCompile 'org.objenesis:objenesis:2.1' testCompile 'org.skyscreamer:jsonassert:1.2.3' @@ -110,7 +108,7 @@ project(':accurest-converters') { compile 'org.apache.commons:commons-lang3:[3.0,)' compile 'commons-io:commons-io:[2.0,)' compile 'dk.brics.automaton:automaton:1.11-8' // needed for Xeger - testCompile 'com.github.tomakehurst:wiremock:2.0.0-beta' + testCompile 'com.blogspot.toomuchcoding:wiremock:0.0.1' testCompile 'org.hamcrest:hamcrest-all:1.3' } } diff --git a/gradle/release.gradle b/gradle/release.gradle index 34e8ae08ef..3300caac3c 100644 --- a/gradle/release.gradle +++ b/gradle/release.gradle @@ -12,7 +12,7 @@ task sourcesJar(type: Jar) { } artifacts { - archives javadocJar, sourcesJar + archives javadocJar, sourcesJar, repackagedJar } signing { From 365af5981aa5d0e95882bf29432b31ac2e182134 Mon Sep 17 00:00:00 2001 From: Marcin Zajaczkowski Date: Wed, 2 Sep 2015 22:46:30 +0200 Subject: [PATCH 077/119] Simplify release mechanism --- build.gradle | 34 +---------------- gradle/release.gradle | 88 ++++++++++++++++--------------------------- 2 files changed, 34 insertions(+), 88 deletions(-) diff --git a/build.gradle b/build.gradle index 8b8d47bb77..7a0bace6ef 100644 --- a/build.gradle +++ b/build.gradle @@ -5,17 +5,12 @@ buildscript { } dependencies { classpath "pl.allegro.tech.build:axion-release-plugin:1.2.2" + classpath "com.bmuschko:gradle-nexus-plugin:2.3" classpath "io.codearte.gradle.nexus:gradle-nexus-staging-plugin:0.5.3" } } apply plugin: "pl.allegro.tech.build.axion-release" -apply plugin: 'io.codearte.nexus-staging' - -nexusStaging { - packageGroup = "io.codearte" - stagingProfileId = '93c08fdebde1ff' -} scmVersion { tag { prefix = "accurest" } @@ -34,11 +29,7 @@ allprojects { subprojects { apply plugin: 'groovy' - apply plugin: 'maven-publish' - - if (!version.contains('SNAPSHOT')) { - apply from: "$rootDir/gradle/release.gradle" - } + apply from: "$rootDir/gradle/release.gradle" group = 'io.codearte.accurest' @@ -60,27 +51,6 @@ subprojects { exclude(group: 'org.codehaus.groovy') } } - - publishing { - publications { - maven(MavenPublication) { - from components.java - pom.withXml { - //#89 - workaround to not to have only runtime dependencies in generated pom.xml - //Known limitation in maven-publish - - http://forums.gradle.org/gradle/topics/maven_publish_plugin_generated_pom_making_dependency_scope_runtime#reply_14120711 - asNode().dependencies.'*'.findAll() { - it.scope.text() == 'runtime' && project.configurations.compile.allDependencies.find { dep -> - dep.name == it.artifactId.text() - } - }.each() { - it.scope*.value = 'compile' - } - } - } - } - } - - uploadArchives.dependsOn { check } } project(':accurest-core') { diff --git a/gradle/release.gradle b/gradle/release.gradle index 34e8ae08ef..abe79f9d5e 100644 --- a/gradle/release.gradle +++ b/gradle/release.gradle @@ -1,64 +1,40 @@ -apply plugin: 'maven' -apply plugin: 'signing' +apply plugin: 'com.bmuschko.nexus' +apply plugin: 'io.codearte.nexus-staging' -task javadocJar(type: Jar) { - classifier = 'javadoc' - from javadoc -} +modifyPom { + project { + name "$project.name" + packaging 'jar' + description 'RESTful Contract Verifier' + url 'https://github.com/Codearte/accurest' + inceptionYear '2014' -task sourcesJar(type: Jar) { - classifier = 'sources' - from sourceSets.main.allSource -} + scm { + connection 'scm:git:git@github.com:Codearte/accurest.git' + developerConnection 'scm:git:git@github.com:Codearte/accurest.git' + url 'https://github.com/Codearte/accurest' + } -artifacts { - archives javadocJar, sourcesJar -} - -signing { - sign configurations.archives -} - -uploadArchives { - repositories { - mavenDeployer { - beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } - - repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") { - authentication(userName: nexusUsername, password: nexusPassword) + licenses { + license { + name 'The Apache License, Version 2.0' + url 'http://www.apache.org/licenses/LICENSE-2.0.txt' } + } - snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") { - authentication(userName: nexusUsername, password: nexusPassword) - } - - pom.project { - name "$project.name" - packaging 'jar' - description 'RESTful Contract Verifier' - url 'http://codearte.github.io/accurest' - - scm { - connection 'scm:git:git@github.com:Codearte/accurest.git' - developerConnection 'scm:git:git@github.com:Codearte/accurest.git' - url 'https://github.com/Codearte/accurest' - } - - licenses { - license { - name 'The Apache License, Version 2.0' - url 'http://www.apache.org/licenses/LICENSE-2.0.txt' - } - } - - developers { - developer { - id 'jkubrynski' - name 'Jakub Kubrynski' - email 'jk ATT codearte DOTT io' - } - } + developers { + developer { + id 'jkubrynski' + name 'Jakub Kubrynski' + email 'jk ATT codearte DOTT io' } } } -} \ No newline at end of file +} + +uploadArchives.dependsOn { check } + +nexusStaging { + packageGroup = "io.codearte" + stagingProfileId = '93c08fdebde1ff' +} From 0129666047b12e93b8c8f958a81a3625a1819e41 Mon Sep 17 00:00:00 2001 From: Marcin Zajaczkowski Date: Wed, 2 Sep 2015 22:49:40 +0200 Subject: [PATCH 078/119] Add marcingrzejszczak as a developer in pom.xml --- gradle/release.gradle | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/gradle/release.gradle b/gradle/release.gradle index abe79f9d5e..8052622761 100644 --- a/gradle/release.gradle +++ b/gradle/release.gradle @@ -28,6 +28,11 @@ modifyPom { name 'Jakub Kubrynski' email 'jk ATT codearte DOTT io' } + developer { + id 'marcingrzejszczak' + name 'Marcin Grzejszczak' + email 'marcin ATT grzejszczak DOTT pl' + } } } } From 9a5afb849de5b898054b42faa03907c14c814ff2 Mon Sep 17 00:00:00 2001 From: Marcin Zajaczkowski Date: Thu, 3 Sep 2015 00:09:48 +0200 Subject: [PATCH 079/119] nexus-staging-plugin is applied only in root project --- build.gradle | 7 +++++++ gradle/release.gradle | 6 ------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/build.gradle b/build.gradle index 7a0bace6ef..e6cb6bf127 100644 --- a/build.gradle +++ b/build.gradle @@ -27,6 +27,13 @@ allprojects { project.version = scmVersion.version } +apply plugin: 'io.codearte.nexus-staging' + +nexusStaging { + packageGroup = "io.codearte" + stagingProfileId = '93c08fdebde1ff' +} + subprojects { apply plugin: 'groovy' apply from: "$rootDir/gradle/release.gradle" diff --git a/gradle/release.gradle b/gradle/release.gradle index 8052622761..32e134b828 100644 --- a/gradle/release.gradle +++ b/gradle/release.gradle @@ -1,5 +1,4 @@ apply plugin: 'com.bmuschko.nexus' -apply plugin: 'io.codearte.nexus-staging' modifyPom { project { @@ -38,8 +37,3 @@ modifyPom { } uploadArchives.dependsOn { check } - -nexusStaging { - packageGroup = "io.codearte" - stagingProfileId = '93c08fdebde1ff' -} From bc51211a504f28c1a7a7cc35af2622102b6d840b Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Wed, 2 Sep 2015 23:57:49 +0200 Subject: [PATCH 080/119] [#123] Removed obsolete accurest-core dependency --- .../io/codearte/accurest/plugin/AccurestGradlePlugin.groovy | 1 - 1 file changed, 1 deletion(-) diff --git a/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/AccurestGradlePlugin.groovy b/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/AccurestGradlePlugin.groovy index f93809b40a..e7c9feafde 100644 --- a/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/AccurestGradlePlugin.groovy +++ b/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/AccurestGradlePlugin.groovy @@ -30,7 +30,6 @@ class AccurestGradlePlugin implements Plugin { createGenerateTestsTask(extension) createAndConfigureGenerateWireMockClientStubsFromDslTask(extension) deprecatedCreateAndConfigureGenerateWiremockClientStubsFromDslTask() - project.dependencies.add("testCompile", "io.codearte.accurest:accurest-core:+") project.dependencies.add("testCompile", "com.blogspot.toomuchcoding:wiremock:0.0.1") project.repositories { jcenter() } project.configurations { From 14deedec760f01c4ea0fe83cb4cc4349587b495e Mon Sep 17 00:00:00 2001 From: Marcin Zajaczkowski Date: Thu, 3 Sep 2015 00:40:19 +0200 Subject: [PATCH 081/119] Travis should run also functional tests --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 074a9aabda..bdf2cb280b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,4 +12,4 @@ cache: - $HOME/.m2 install: ./gradlew assemble -script: ./gradlew check --stacktrace --info --continue \ No newline at end of file +script: ./gradlew check funcTest --stacktrace --info --continue From 7122e3006ed05d4622edd35a72d573ff7b44526d Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Thu, 3 Sep 2015 00:46:48 +0200 Subject: [PATCH 082/119] Fixed the functional tests --- .../accurest/dsl/WireMockGroovyDslSpec.groovy | 99 ++++++++++--------- .../plugin/BasicFunctionalSpec.groovy | 42 ++++---- .../accurest/util/AssertionUtil.groovy | 12 +++ build.gradle | 11 ++- settings.gradle | 2 +- 5 files changed, 94 insertions(+), 72 deletions(-) create mode 100644 accurest-testing-utils/src/main/groovy/io/codearte/accurest/util/AssertionUtil.groovy diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy index 3356c1a7c9..548f309b00 100755 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy @@ -1,7 +1,8 @@ package io.codearte.accurest.dsl + import groovy.json.JsonBuilder import groovy.json.JsonSlurper -import org.skyscreamer.jsonassert.JSONAssert +import io.codearte.accurest.util.AssertionUtil import spock.lang.Issue class WireMockGroovyDslSpec extends WireMockSpec { @@ -35,7 +36,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() then: - JSONAssert.assertEquals(''' + AssertionUtil.assertThatJsonsAreEqual(''' { "request" : { "urlPattern" : "/[0-9]{2}", @@ -49,7 +50,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { } } } - ''', wireMockStub, false) + ''', wireMockStub) and: stubMappingIsValidWireMockStub(wireMockStub) } @@ -80,7 +81,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() then: - JSONAssert.assertEquals(''' + AssertionUtil.assertThatJsonsAreEqual(''' { "request" : { "url" : "/ingredients", @@ -96,7 +97,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { "body" : "{\\"ingredients\\":[{\\"type\\":\\"MALT\\",\\"quantity\\":100},{\\"type\\":\\"WATER\\",\\"quantity\\":200},{\\"type\\":\\"HOP\\",\\"quantity\\":300},{\\"type\\":\\"YIEST\\",\\"quantity\\":400}]}" } } -''', wireMockStub, false) +''', wireMockStub) and: stubMappingIsValidWireMockStub(wireMockStub) } @@ -128,7 +129,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() then: - JSONAssert.assertEquals(''' + AssertionUtil.assertThatJsonsAreEqual(''' { "request": { "method": "POST", @@ -149,7 +150,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { "body": "{\\"paymentId\\":\\"4\\",\\"foundExistingPayment\\":false}" } } -''', wireMockStub, false) +''', wireMockStub) and: stubMappingIsValidWireMockStub(wireMockStub) } @@ -180,7 +181,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() then: - JSONAssert.assertEquals((''' + AssertionUtil.assertThatJsonsAreEqual((''' { "request" : { "urlPattern" : "/[0-9]{2}", @@ -194,7 +195,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { } } } -'''), wireMockStub, false) +'''), wireMockStub) and: stubMappingIsValidWireMockStub(wireMockStub) } @@ -227,7 +228,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() then: - JSONAssert.assertEquals(''' + AssertionUtil.assertThatJsonsAreEqual(''' { "request" : { "urlPattern" : "/[0-9]{2}", @@ -244,7 +245,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { } } } -''', wireMockStub, false) +''', wireMockStub) and: stubMappingIsValidWireMockStub(wireMockStub) } @@ -275,7 +276,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() then: - JSONAssert.assertEquals((''' + AssertionUtil.assertThatJsonsAreEqual((''' { "request" : { "urlPattern" : "/[0-9]{2}", @@ -294,7 +295,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { "status" : 200 } } - '''), wireMockStub, false) + '''), wireMockStub) and: stubMappingIsValidWireMockStub(wireMockStub) } @@ -321,7 +322,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: String json = toWireMockClientJsonStub(groovyDsl) then: - JSONAssert.assertEquals((''' + AssertionUtil.assertThatJsonsAreEqual((''' { "request" : { "url" : "/users", @@ -339,7 +340,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { "status" : 200 } } - '''), json, false) + '''), json) and: stubMappingIsValidWireMockStub(json) } @@ -364,7 +365,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: String json = toWireMockClientJsonStub(groovyDsl) then: - JSONAssert.assertEquals((''' + AssertionUtil.assertThatJsonsAreEqual((''' { "request": { "method": "GET", @@ -384,7 +385,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { "status": 200 } } - '''), json, false) + '''), json) and: stubMappingIsValidWireMockStub(json) } @@ -406,7 +407,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: String json = toWireMockClientJsonStub(groovyDsl) then: - JSONAssert.assertEquals((''' + AssertionUtil.assertThatJsonsAreEqual((''' { "request": { "method": "GET", @@ -421,7 +422,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { "status": 200 } } - '''), json, false) + '''), json) and: stubMappingIsValidWireMockStub(json) } @@ -443,7 +444,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: String json = toWireMockClientJsonStub(groovyDsl) then: - JSONAssert.assertEquals((''' + AssertionUtil.assertThatJsonsAreEqual((''' { "request": { "method": "GET", @@ -454,7 +455,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { "body":"Jozo<test>" } } - '''), json, false) + '''), json) and: stubMappingIsValidWireMockStub(json) } @@ -474,7 +475,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: String json = toWireMockClientJsonStub(groovyDsl) then: - JSONAssert.assertEquals((''' + AssertionUtil.assertThatJsonsAreEqual((''' { "request": { "method": "GET", @@ -489,7 +490,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { "status": 200 } } - '''), json, false) + '''), json) and: stubMappingIsValidWireMockStub(json) } @@ -511,7 +512,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: String json = toWireMockClientJsonStub(groovyDsl) then: - JSONAssert.assertEquals((''' + AssertionUtil.assertThatJsonsAreEqual((''' { "request": { "method": "GET", @@ -526,7 +527,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { "status": 200 } } - '''), json, false) + '''), json) and: stubMappingIsValidWireMockStub(json) } @@ -559,7 +560,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() then: - JSONAssert.assertEquals((''' + AssertionUtil.assertThatJsonsAreEqual((''' { "request" : { "urlPattern" : "/[0-9]{2}", @@ -576,7 +577,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { } } } -'''), wireMockStub, false) +'''), wireMockStub) and: stubMappingIsValidWireMockStub(wireMockStub) } @@ -614,7 +615,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() then: - JSONAssert.assertEquals((''' + AssertionUtil.assertThatJsonsAreEqual((''' { "request" : { "url" : "/fraudcheck", @@ -638,7 +639,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { } } } -'''), wireMockStub, false) +'''), wireMockStub) and: stubMappingIsValidWireMockStub(wireMockStub) } @@ -668,7 +669,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: def json = toWireMockClientJsonStub(groovyDsl) then: - JSONAssert.assertEquals((''' + AssertionUtil.assertThatJsonsAreEqual((''' { "request": { "method": "GET", @@ -704,7 +705,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { "status": 200, } } - '''), json, false) + '''), json) and: stubMappingIsValidWireMockStub(json) } @@ -723,7 +724,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: def json = toWireMockClientJsonStub(groovyDsl) then: - JSONAssert.assertEquals((''' + AssertionUtil.assertThatJsonsAreEqual((''' { "request": { "method": "GET", @@ -733,7 +734,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { "status": 200, } } - '''), json, false) + '''), json) and: stubMappingIsValidWireMockStub(json) } @@ -752,7 +753,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: def json = toWireMockClientJsonStub(groovyDsl) then: - JSONAssert.assertEquals((''' + AssertionUtil.assertThatJsonsAreEqual((''' { "request": { "method": "GET", @@ -762,7 +763,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { "status": 200, } } - '''), json, false) + '''), json) and: stubMappingIsValidWireMockStub(json) } @@ -898,7 +899,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: def json = toWireMockClientJsonStub(groovyDsl) then: - JSONAssert.assertEquals((''' + AssertionUtil.assertThatJsonsAreEqual((''' { "request": { "method": "GET", @@ -916,7 +917,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { "status": 200, } } - '''), json, false) + '''), json) and: stubMappingIsValidWireMockStub(json) } @@ -962,7 +963,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() then: - JSONAssert.assertEquals((''' + AssertionUtil.assertThatJsonsAreEqual((''' { "request" : { "urlPattern" : "/[0-9]{2}", @@ -993,7 +994,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { } } } - '''), wireMockStub, false) + '''), wireMockStub) } def 'should use regexp matches when request body match is defined using a map with a pattern'() { @@ -1026,7 +1027,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: def json = toWireMockClientJsonStub(groovyDsl) then: - JSONAssert.assertEquals((''' + AssertionUtil.assertThatJsonsAreEqual((''' { "request" : { "url" : "/reissue-payment-order", @@ -1053,7 +1054,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { } } } - '''), json, false) + '''), json) } def "should generate stub for empty body"() { @@ -1071,7 +1072,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: def json = toWireMockClientJsonStub(groovyDsl) then: - JSONAssert.assertEquals((''' + AssertionUtil.assertThatJsonsAreEqual((''' { "request": { "method": "POST", @@ -1086,7 +1087,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { "status": 406 } } -'''), json, false) +'''), json) } def "should generate stub with priority"() { @@ -1104,7 +1105,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: def json = toWireMockClientJsonStub(groovyDsl) then: - JSONAssert.assertEquals((''' + AssertionUtil.assertThatJsonsAreEqual((''' { "priority": 9, "request": { @@ -1115,7 +1116,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { "status": 406 } } - '''), json, false) + '''), json) } @Issue("#127") @@ -1135,7 +1136,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() then: - JSONAssert.assertEquals((''' + AssertionUtil.assertThatJsonsAreEqual((''' { "request" : { "method" : "POST", @@ -1147,7 +1148,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { "status" : 200 } } - '''), wireMockStub, false) + '''), wireMockStub) and: stubMappingIsValidWireMockStub(wireMockStub) } @@ -1169,7 +1170,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { when: String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() then: - JSONAssert.assertEquals((''' + AssertionUtil.assertThatJsonsAreEqual((''' { "request": { "method": "POST", @@ -1183,7 +1184,7 @@ class WireMockGroovyDslSpec extends WireMockSpec { "status": 200 } } - '''), wireMockStub, false) + '''), wireMockStub) and: stubMappingIsValidWireMockStub(wireMockStub) } diff --git a/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/BasicFunctionalSpec.groovy b/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/BasicFunctionalSpec.groovy index 5b101daa03..a54d20308e 100755 --- a/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/BasicFunctionalSpec.groovy +++ b/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/BasicFunctionalSpec.groovy @@ -1,6 +1,6 @@ package io.codearte.accurest.plugin -import groovy.json.JsonSlurper +import io.codearte.accurest.util.AssertionUtil import nebula.test.IntegrationSpec import spock.lang.Stepwise @@ -41,26 +41,26 @@ class BasicFunctionalSpec extends IntegrationSpec { runTasksSuccessfully('generateWireMockClientStubs') then: def generatedClientJsonStub = file(GENERATED_CLIENT_JSON_STUB).text - new JsonSlurper().parseText(generatedClientJsonStub) == new JsonSlurper().parseText(""" -{ - "priority": 2, - "request": { - "method": "PUT", - "headers": { - "Content-Type": { - "equalTo": "application/json" - } - }, - "url": "/api/12", - "bodyPatterns": [ - { "equalToJson": "[{\\"text\\":\\"Gonna see you at Warsaw\\"}]" } - ] - }, - "response": { - "status": 200 - } -} -""") + AssertionUtil.assertThatJsonsAreEqual(""" + { + "request" : { + "url" : "/api/12", + "method" : "PUT", + "bodyPatterns" : [ { + "matchesJsonPath" : "\$[*][?(@.text == 'Gonna see you at Warsaw')]" + } ], + "headers" : { + "Content-Type" : { + "equalTo" : "application/json" + } + } + }, + "response" : { + "status" : 200 + }, + "priority" : 2 + } + """, generatedClientJsonStub) } def "tasks should be up-to-date when appropriate"() { diff --git a/accurest-testing-utils/src/main/groovy/io/codearte/accurest/util/AssertionUtil.groovy b/accurest-testing-utils/src/main/groovy/io/codearte/accurest/util/AssertionUtil.groovy new file mode 100644 index 0000000000..4c632a4de3 --- /dev/null +++ b/accurest-testing-utils/src/main/groovy/io/codearte/accurest/util/AssertionUtil.groovy @@ -0,0 +1,12 @@ +package io.codearte.accurest.util + +import org.skyscreamer.jsonassert.JSONAssert + +class AssertionUtil { + + private static boolean NON_STRICT = false + + public static void assertThatJsonsAreEqual(String expected, String actual) { + JSONAssert.assertEquals(expected, actual, NON_STRICT) + } +} diff --git a/build.gradle b/build.gradle index ed8eb470e7..7f78c6ad8a 100644 --- a/build.gradle +++ b/build.gradle @@ -74,7 +74,15 @@ project(':accurest-core') { compile 'com.blogspot.toomuchcoding:wiremock:0.0.1' testCompile 'cglib:cglib-nodep:2.2' testCompile 'org.objenesis:objenesis:2.1' - testCompile 'org.skyscreamer:jsonassert:1.2.3' + testCompile project(':accurest-testing-utils') + } + +} + +project(':accurest-testing-utils') { + + dependencies { + compile 'org.skyscreamer:jsonassert:1.2.3' } } @@ -99,6 +107,7 @@ project(':accurest-gradle-plugin') { testCompile('com.netflix.nebula:nebula-test:2.2.1') { exclude(group: 'org.spockframework') } + testCompile project(':accurest-testing-utils') } test { diff --git a/settings.gradle b/settings.gradle index 148580177b..7a36841ba8 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,3 +1,3 @@ -include "accurest-core", "accurest-gradle-plugin", 'accurest-converters' +include "accurest-core", "accurest-gradle-plugin", 'accurest-converters', 'accurest-testing-utils' rootProject.name = "accurest" From 7782d82ba43960a6319e01b7821645fd5450bf2a Mon Sep 17 00:00:00 2001 From: Marcin Zajaczkowski Date: Thu, 3 Sep 2015 00:56:41 +0200 Subject: [PATCH 083/119] Release version: 0.9.0 [ci skip] From f824415523cf28a58cea042c3adac4a24f744ae8 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Thu, 3 Sep 2015 14:17:59 +0200 Subject: [PATCH 084/119] [PPL-202] Fixed GString support in body with regex --- .../builder/SpockMethodBodyBuilder.groovy | 4 +-- .../dsl/WireMockRequestStubStrategy.groovy | 8 ++--- .../dsl/internal/RegexPatterns.groovy | 2 +- ...groovy => JsonToJsonPathsConverter.groovy} | 15 ++++++-- .../accurest/util/RegexpBuilders.groovy | 33 +++++++++++++---- .../MockMvcSpockMethodBuilderSpec.groovy | 35 +++++++++++++++++++ ...vy => JsonToJsonPathsConverterSpec.groovy} | 12 +++---- build.gradle | 3 +- 8 files changed, 88 insertions(+), 24 deletions(-) rename accurest-core/src/main/groovy/io/codearte/accurest/util/{JsonPathJsonConverter.groovy => JsonToJsonPathsConverter.groovy} (90%) rename accurest-core/src/test/groovy/io/codearte/accurest/util/{JsonPathJsonConverterSpec.groovy => JsonToJsonPathsConverterSpec.groovy} (89%) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy index 49df619015..dbd0663ac9 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy @@ -6,7 +6,7 @@ import io.codearte.accurest.dsl.GroovyDsl import io.codearte.accurest.dsl.internal.* import io.codearte.accurest.util.ContentType import io.codearte.accurest.util.MapConverter -import io.codearte.accurest.util.JsonPathJsonConverter +import io.codearte.accurest.util.JsonToJsonPathsConverter import io.codearte.accurest.util.JsonPaths import static io.codearte.accurest.util.ContentUtils.* @@ -86,7 +86,7 @@ abstract class SpockMethodBodyBuilder { } if (contentType == ContentType.JSON) { appendJsonPath(bb, responseAsString) - JsonPaths jsonPaths = JsonPathJsonConverter.transformToJsonPathWithTestsSideValues(responseBody) + JsonPaths jsonPaths = JsonToJsonPathsConverter.transformToJsonPathWithTestsSideValues(responseBody) jsonPaths.each { it.buildJsonPathComparison('parsedJson').each { bb.addLine(it) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockRequestStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockRequestStubStrategy.groovy index 923b614f7a..c87a1cebac 100755 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockRequestStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockRequestStubStrategy.groovy @@ -9,14 +9,14 @@ import groovy.transform.TypeCheckingMode import io.codearte.accurest.dsl.internal.* import io.codearte.accurest.util.ContentType import io.codearte.accurest.util.ContentUtils -import io.codearte.accurest.util.JsonPathJsonConverter +import io.codearte.accurest.util.JsonToJsonPathsConverter import io.codearte.accurest.util.JsonPaths import io.codearte.accurest.util.MapConverter import java.util.regex.Pattern import static io.codearte.accurest.util.ContentUtils.* -import static io.codearte.accurest.util.RegexpBuilders.buildGStringRegexpMatch +import static io.codearte.accurest.util.RegexpBuilders.buildGStringRegexpForStubSide import static io.codearte.accurest.util.RegexpBuilders.buildJSONRegexpMatch @TypeChecked @@ -53,7 +53,7 @@ class WireMockRequestStubStrategy extends BaseWireMockStubStrategy { } ContentType contentType = tryToGetContentType() if (contentType == ContentType.JSON) { - JsonPaths values = JsonPathJsonConverter.transformToJsonPathWithStubsSideValues(getMatchingStrategyFromBody(request.body)?.clientValue) + JsonPaths values = JsonToJsonPathsConverter.transformToJsonPathWithStubsSideValues(getMatchingStrategyFromBody(request.body)?.clientValue) if (values.empty) { requestPattern.bodyPatterns = [new ValuePattern(jsonCompareMode: org.skyscreamer.jsonassert.JSONCompareMode.LENIENT, equalToJson: JsonOutput.toJson(getMatchingStrategy(request.body.clientValue).clientValue) ) ] @@ -183,7 +183,7 @@ class WireMockRequestStubStrategy extends BaseWireMockStubStrategy { case ContentType.JSON: return new MatchingStrategy(buildJSONRegexpMatch(value), MatchingStrategy.Type.MATCHING) case ContentType.UNKNOWN: - return new MatchingStrategy(buildGStringRegexpMatch(value), MatchingStrategy.Type.MATCHING) + return new MatchingStrategy(buildGStringRegexpForStubSide(value), MatchingStrategy.Type.MATCHING) case ContentType.XML: throw new IllegalStateException("XML pattern matching is not implemented yet") } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/RegexPatterns.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/RegexPatterns.groovy index 210668a2a9..432dee266d 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/RegexPatterns.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/RegexPatterns.groovy @@ -9,7 +9,7 @@ class RegexPatterns { private 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])'); private static final Pattern HOSTNAME_PATTERN = Pattern.compile('((http[s]?|ftp):\\/)\\/?([^:\\/\\s]+)(:[0-9]{1,5})?'); - private static final Pattern EMAIL = Pattern.compile('[_]*([a-z0-9]+(\\.|_*)?)+@([a-z][a-z0-9-]+(\\.|-*\\.))+[a-z]{2,6}'); + private static final Pattern EMAIL = Pattern.compile('[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}'); private static final Pattern URL = Pattern.compile('((www\\.|(http|https|ftp|news|file)+\\:\\/\\/)[_.a-z0-9-]+\\.[a-z0-9\\/_:@=.+?,##%&~-]*[^.|\\\'|\\# |!|\\(|?|,| |>|<|;|\\)])'); String ipAddress() { diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonPathJsonConverter.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonToJsonPathsConverter.groovy similarity index 90% rename from accurest-core/src/main/groovy/io/codearte/accurest/util/JsonPathJsonConverter.groovy rename to accurest-core/src/main/groovy/io/codearte/accurest/util/JsonToJsonPathsConverter.groovy index 28625a1cbf..6a749346a7 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonPathJsonConverter.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonToJsonPathsConverter.groovy @@ -8,7 +8,7 @@ import java.util.regex.Pattern /** * @author Marcin Grzejszczak */ -class JsonPathJsonConverter { +class JsonToJsonPathsConverter { private static final Boolean SERVER_SIDE = false private static final Boolean CLIENT_SIDE = true @@ -42,11 +42,18 @@ class JsonPathJsonConverter { private static Object getClientOrServerSideValues(json, boolean clientSide) { return MapConverter.transformValues(json) { - boolean dslProp = it instanceof DslProperty - if (dslProp) { + if (it instanceof DslProperty) { DslProperty dslProperty = ((DslProperty) it) return clientSide ? getClientOrServerSideValues(dslProperty.clientValue, clientSide) : getClientOrServerSideValues(dslProperty.serverValue, clientSide) + } else if (it instanceof GString) { + return ContentUtils.extractValue(it , null, { + if (it instanceof DslProperty) { + return clientSide ? + getClientOrServerSideValues((it as DslProperty).clientValue, clientSide) : getClientOrServerSideValues((it as DslProperty).serverValue, clientSide) + } + return it + }) } return it } @@ -142,6 +149,8 @@ class JsonPathJsonConverter { protected static String compareWith(Object value) { if (value instanceof Pattern) { return """=~ /${(value as Pattern).pattern()}/""" + } else if (value instanceof GString) { + return """=~ /${RegexpBuilders.buildGStringRegexpForTestSide(value)}/""" } return """== ${potentiallyWrappedWithQuotesValue(value)}""" } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/util/RegexpBuilders.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/RegexpBuilders.groovy index 47cb595220..6c8b703fca 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/util/RegexpBuilders.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/RegexpBuilders.groovy @@ -10,29 +10,48 @@ import static org.apache.commons.lang3.StringEscapeUtils.escapeJson public class RegexpBuilders { - public static String buildGStringRegexpMatch(GString gString) { + public static String buildGStringRegexpForStubSide(GString gString) { new GStringImpl( - gString.values.collect(this.&buildGStringRegexpMatch) as Object[], + gString.values.collect(this.&buildGStringRegexpForStubSide) as Object[], gString.strings.collect(this.&escapeSpecialRegexChars) as String[] ) } - public static String buildGStringRegexpMatch(Pattern pattern) { + public static String buildGStringRegexpForStubSide(Pattern pattern) { return pattern.pattern() } - public static String buildGStringRegexpMatch(DslProperty dslProperty) { - return buildGStringRegexpMatch(dslProperty.clientValue) + public static String buildGStringRegexpForStubSide(DslProperty dslProperty) { + return buildGStringRegexpForStubSide(dslProperty.clientValue) } - public static String buildGStringRegexpMatch(Object o) { + public static String buildGStringRegexpForStubSide(Object o) { return escapeSpecialRegexChars(o.toString()) } + public static String buildGStringRegexpForTestSide(GString gString) { + new GStringImpl( + gString.values.collect(this.&buildGStringRegexpForTestSide) as Object[], + gString.strings.collect(this.&escapeSpecialRegexChars) as String[] + ) + } + + public static String buildGStringRegexpForTestSide(Pattern pattern) { + return pattern.pattern() + } + + public static String buildGStringRegexpForTestSide(DslProperty dslProperty) { + return buildGStringRegexpForTestSide(dslProperty.clientValue) + } + + public static String buildGStringRegexpForTestSide(Object o) { + return o.toString().replaceAll('\\\\', '\\\\\\\\') + } + private final static Pattern SPECIAL_REGEX_CHARS = Pattern.compile('[{}()\\[\\].+*?^$\\\\|]') private static String escapeSpecialRegexChars(String str) { - return SPECIAL_REGEX_CHARS.matcher(str).replaceAll('\\\\$0') + return SPECIAL_REGEX_CHARS.matcher(str).replaceAll('\\\\\\\\$0') } private final static String WS = /\s*/ diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy index dcbf988168..5d25d94874 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy @@ -429,4 +429,39 @@ class MockMvcSpockMethodBuilderSpec extends Specification { spockTest.contains('''$.errors[*][?(@.property == 'bank_account_number')]''') spockTest.contains('''$.errors[*][?(@.message == 'incorrect_format')]''') } + + def "should resolve properties in GString with regular expression"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + priority 1 + request { + method 'POST' + url '/users/password' + headers { + header 'Content-Type': 'application/json' + } + body( + email: $(client(regex(email())), server('not.existing@user.com')), + callback_url: $(client(regex(hostname())), server('http://partners.com')) + ) + } + response { + status 404 + headers { + header 'Content-Type': 'application/json' + } + body( + code: 4, + 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('''$[?(@.message =~ /User not found by email = \\\\[[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,4}\\\\]/)]''') + } } diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/util/JsonPathJsonConverterSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/util/JsonToJsonPathsConverterSpec.groovy similarity index 89% rename from accurest-core/src/test/groovy/io/codearte/accurest/util/JsonPathJsonConverterSpec.groovy rename to accurest-core/src/test/groovy/io/codearte/accurest/util/JsonToJsonPathsConverterSpec.groovy index a7e9f621d7..5348c77fd5 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/util/JsonPathJsonConverterSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/util/JsonToJsonPathsConverterSpec.groovy @@ -11,12 +11,12 @@ import spock.lang.Unroll import java.util.regex.Pattern -class JsonPathJsonConverterSpec extends Specification { +class JsonToJsonPathsConverterSpec extends Specification { @Unroll def 'should convert a json with list as root to a map of path to value'() { when: - JsonPaths pathAndValues = JsonPathJsonConverter.transformToJsonPathWithTestsSideValues(new JsonSlurper().parseText(json)) + JsonPaths pathAndValues = JsonToJsonPathsConverter.transformToJsonPathWithTestsSideValues(new JsonSlurper().parseText(json)) then: pathAndValues['''$[*].some.nested[?(@.json == 'with value')]'''] == 'with value' pathAndValues['''$[*].some.nested[?(@.anothervalue == 4)]'''] == 4 @@ -94,7 +94,7 @@ class JsonPathJsonConverterSpec extends Specification { } ''' when: - JsonPaths pathAndValues = JsonPathJsonConverter.transformToJsonPathWithTestsSideValues(new JsonSlurper().parseText(json)) + JsonPaths pathAndValues = JsonToJsonPathsConverter.transformToJsonPathWithTestsSideValues(new JsonSlurper().parseText(json)) then: pathAndValues['''$.some.nested[?(@.json == 'with value')]'''] == 'with value' pathAndValues['''$.some.nested[?(@.anothervalue == 4)]'''] == 4 @@ -112,7 +112,7 @@ class JsonPathJsonConverterSpec extends Specification { } ''' when: - JsonPaths pathAndValues = JsonPathJsonConverter.transformToJsonPathWithTestsSideValues(new JsonSlurper().parseText(json)) + JsonPaths pathAndValues = JsonToJsonPathsConverter.transformToJsonPathWithTestsSideValues(new JsonSlurper().parseText(json)) then: pathAndValues['''$.items[?(@ == 'HOP')]'''] == 'HOP' and: @@ -131,7 +131,7 @@ class JsonPathJsonConverterSpec extends Specification { } ''' when: - JsonPaths pathAndValues = JsonPathJsonConverter.transformToJsonPathWithTestsSideValues(new JsonSlurper().parseText(json)) + JsonPaths pathAndValues = JsonToJsonPathsConverter.transformToJsonPathWithTestsSideValues(new JsonSlurper().parseText(json)) then: pathAndValues['''$.errors[*][?(@.property == 'email')]'''] == 'email' pathAndValues['''$.errors[*][?(@.message == 'inconsistent value')]'''] == 'inconsistent value' @@ -175,7 +175,7 @@ class JsonPathJsonConverterSpec extends Specification { ] ] when: - JsonPaths pathAndValues = JsonPathJsonConverter.transformToJsonPathWithTestsSideValues(json) + JsonPaths pathAndValues = JsonToJsonPathsConverter.transformToJsonPathWithTestsSideValues(json) then: pathAndValues['''$[*].some.nested[?(@.json == 'with value')]'''] == 'with value' pathAndValues['''$[*].some.nested[?(@.anothervalue == 4)]'''] == 4 diff --git a/build.gradle b/build.gradle index 7f78c6ad8a..c1739aa58c 100644 --- a/build.gradle +++ b/build.gradle @@ -24,7 +24,8 @@ scmVersion { } allprojects { - project.version = scmVersion.version + //project.version = scmVersion.version + project.version = '0.9.1-SNAPSHOT' } apply plugin: 'io.codearte.nexus-staging' From 052068db462de79c2388aadf78a49af8182a7790 Mon Sep 17 00:00:00 2001 From: Marcin Zajaczkowski Date: Wed, 2 Sep 2015 22:46:30 +0200 Subject: [PATCH 085/119] Simplify release mechanism --- gradle/release.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/gradle/release.gradle b/gradle/release.gradle index 32e134b828..e3f5159bb1 100644 --- a/gradle/release.gradle +++ b/gradle/release.gradle @@ -37,3 +37,4 @@ modifyPom { } uploadArchives.dependsOn { check } + From f9c7ebc64450e7e21022dde9c61828ff967cd546 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Thu, 3 Sep 2015 14:17:59 +0200 Subject: [PATCH 086/119] Fixed issues with GString and Regex --- .../MockMvcSpockMethodBodyBuilder.groovy | 7 +- .../builder/SpockMethodBodyBuilder.groovy | 5 + .../dsl/BaseWireMockStubStrategy.groovy | 20 +++- .../dsl/WireMockRequestStubStrategy.groovy | 28 ++--- .../util/JsonToJsonPathsConverter.groovy | 22 +--- .../accurest/util/MapConverter.groovy | 18 +++ .../JaxRsClientSpockMethodBuilderSpec.groovy | 33 +++++- .../MockMvcSpockMethodBuilderSpec.groovy | 63 ++++++++++- .../accurest/dsl/WireMockGroovyDslSpec.groovy | 106 +++++++++++++++++- ...pec.groovy => WireMockStubVerifier.groovy} | 5 +- 10 files changed, 260 insertions(+), 47 deletions(-) rename accurest-core/src/test/groovy/io/codearte/accurest/dsl/{WireMockSpec.groovy => WireMockStubVerifier.groovy} (82%) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBodyBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBodyBuilder.groovy index d686bcc54e..1b2e9a9d24 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBodyBuilder.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBodyBuilder.groovy @@ -1,5 +1,4 @@ package io.codearte.accurest.builder - import groovy.transform.PackageScope import groovy.transform.TypeChecked import groovy.transform.TypeCheckingMode @@ -23,7 +22,7 @@ class MockMvcSpockMethodBodyBuilder extends SpockMethodBodyBuilder { bb.addLine('def request = given()') bb.indent() request.headers?.collect { Header header -> - bb.addLine(".header('${header.name}', '${header.serverValue}')") + bb.addLine(".header('${getTestSideValue(header.name)}', '${getTestSideValue(header.serverValue)}')") } if (request.body) { bb.addLine(".body('$bodyAsString')") @@ -67,9 +66,9 @@ class MockMvcSpockMethodBodyBuilder extends SpockMethodBodyBuilder { protected String buildUrl(Request request) { if (request.url) - return request.url.serverValue; + return getTestSideValue(request.url.serverValue) if (request.urlPath) - return buildUrlFromUrlPath(request.urlPath) + return getTestSideValue(buildUrlFromUrlPath(request.urlPath)) throw new IllegalStateException("URL is not set!") } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy index dbd0663ac9..2789e52a2d 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy @@ -17,6 +17,8 @@ import static io.codearte.accurest.util.ContentUtils.* @TypeChecked abstract class SpockMethodBodyBuilder { + private static final Boolean TEST_SIDE = false + protected final Request request protected final Response response @@ -200,4 +202,7 @@ abstract class SpockMethodBodyBuilder { return contentType } + protected String getTestSideValue(Object object) { + return MapConverter.getClientOrServerSideValues(object, TEST_SIDE).toString() + } } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/BaseWireMockStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/BaseWireMockStubStrategy.groovy index c69e5539fa..c485285281 100755 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/BaseWireMockStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/BaseWireMockStubStrategy.groovy @@ -6,6 +6,8 @@ import io.codearte.accurest.dsl.internal.DslProperty import io.codearte.accurest.dsl.internal.Header import io.codearte.accurest.dsl.internal.Headers import io.codearte.accurest.util.ContentType +import io.codearte.accurest.util.ContentUtils +import io.codearte.accurest.util.MapConverter import java.util.regex.Pattern @@ -15,6 +17,12 @@ import static io.codearte.accurest.util.MapConverter.transformValues @TypeChecked abstract class BaseWireMockStubStrategy { + private static final Boolean STUB_SIDE = true + + protected getStubSideValue(Object object) { + return MapConverter.getClientOrServerSideValues(object, STUB_SIDE) + } + private static Closure transform = { it instanceof DslProperty ? transformValues(it.clientValue, transform) : it } @@ -58,7 +66,7 @@ abstract class BaseWireMockStubStrategy { } public String parseBody(Map map, ContentType contentType) { - def transformedMap = transformValues(map, transform) + def transformedMap = MapConverter.getClientOrServerSideValues(map, true) return parseBody(toJson(transformedMap), contentType) } @@ -82,4 +90,14 @@ abstract class BaseWireMockStubStrategy { return new JsonBuilder(value).toString() } + protected ContentType tryToGetContentType(Object body, Headers headers) { + ContentType contentType = ContentUtils.recognizeContentTypeFromHeader(headers) + if (contentType == ContentType.UNKNOWN) { + if (!body) { + return ContentType.UNKNOWN + } + return ContentUtils.getClientContentType(body) + } + return contentType + } } \ No newline at end of file diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockRequestStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockRequestStubStrategy.groovy index c87a1cebac..cf69737c95 100755 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockRequestStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockRequestStubStrategy.groovy @@ -51,7 +51,7 @@ class WireMockRequestStubStrategy extends BaseWireMockStubStrategy { if (!request.body) { return } - ContentType contentType = tryToGetContentType() + ContentType contentType = tryToGetContentType(request.body.clientValue, request.headers) if (contentType == ContentType.JSON) { JsonPaths values = JsonToJsonPathsConverter.transformToJsonPathWithStubsSideValues(getMatchingStrategyFromBody(request.body)?.clientValue) if (values.empty) { @@ -70,17 +70,6 @@ class WireMockRequestStubStrategy extends BaseWireMockStubStrategy { } } - private ContentType tryToGetContentType() { - ContentType contentType = recognizeContentTypeFromHeader(request.headers) - if (contentType == ContentType.UNKNOWN) { - if (!request.body.clientValue) { - return ContentType.UNKNOWN - } - return ContentUtils.getClientContentType(request.body.clientValue) - } - return contentType - } - private void appendHeaders(RequestPattern requestPattern) { if(!request.headers) { return @@ -93,12 +82,12 @@ class WireMockRequestStubStrategy extends BaseWireMockStubStrategy { private void appendUrl(RequestPattern requestPattern) { Object urlPath = request?.urlPath?.clientValue if (urlPath) { - requestPattern.setUrlPath(urlPath.toString()) + requestPattern.setUrlPath(getStubSideValue(urlPath.toString()).toString()) } if(!request.url) { return } - Object url = request?.url?.clientValue + Object url = getUrlIfGstring(request?.url?.clientValue) if(url instanceof Pattern) { requestPattern.setUrlPattern(url.pattern()) } else { @@ -106,6 +95,17 @@ class WireMockRequestStubStrategy extends BaseWireMockStubStrategy { } } + private Object getUrlIfGstring(Object clientSide) { + if (clientSide instanceof GString) { + if (clientSide.values.any { getStubSideValue(it) instanceof Pattern }) { + return Pattern.compile(getStubSideValue(clientSide).toString()) + } else { + return getStubSideValue(clientSide).toString() + } + } + return clientSide + } + private void appendQueryParameters(RequestPattern requestPattern) { QueryParameters queryParameters = request?.urlPath?.queryParameters ?: request?.url?.queryParameters queryParameters?.parameters?.each { diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonToJsonPathsConverter.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonToJsonPathsConverter.groovy index 6a749346a7..32affecef2 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonToJsonPathsConverter.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonToJsonPathsConverter.groovy @@ -1,7 +1,6 @@ package io.codearte.accurest.util import groovy.json.JsonSlurper -import io.codearte.accurest.dsl.internal.DslProperty import io.codearte.accurest.dsl.internal.ExecutionProperty import java.util.regex.Pattern @@ -29,7 +28,7 @@ class JsonToJsonPathsConverter { return new JsonPaths() } JsonPaths pathsAndValues = [] as Set - Object convertedJson = getClientOrServerSideValues(json, clientSide) + Object convertedJson = MapConverter.getClientOrServerSideValues(json, clientSide) traverseRecursivelyForKey(convertedJson, ROOT_JSON_PATH_ELEMENT) { String key, Object value -> if (value instanceof ExecutionProperty) { return @@ -40,25 +39,6 @@ class JsonToJsonPathsConverter { return pathsAndValues } - private static Object getClientOrServerSideValues(json, boolean clientSide) { - return MapConverter.transformValues(json) { - if (it instanceof DslProperty) { - DslProperty dslProperty = ((DslProperty) it) - return clientSide ? - getClientOrServerSideValues(dslProperty.clientValue, clientSide) : getClientOrServerSideValues(dslProperty.serverValue, clientSide) - } else if (it instanceof GString) { - return ContentUtils.extractValue(it , null, { - if (it instanceof DslProperty) { - return clientSide ? - getClientOrServerSideValues((it as DslProperty).clientValue, clientSide) : getClientOrServerSideValues((it as DslProperty).serverValue, clientSide) - } - return it - }) - } - return it - } - } - protected static def traverseRecursively(Class parentType, String key, def value, Closure closure) { if (value instanceof String && value) { try { diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/util/MapConverter.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/MapConverter.groovy index 14568e7584..ca0717ba68 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/util/MapConverter.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/MapConverter.groovy @@ -43,4 +43,22 @@ class MapConverter { } } + static Object getClientOrServerSideValues(json, boolean clientSide) { + return transformValues(json) { + if (it instanceof DslProperty) { + DslProperty dslProperty = ((DslProperty) it) + return clientSide ? + getClientOrServerSideValues(dslProperty.clientValue, clientSide) : getClientOrServerSideValues(dslProperty.serverValue, clientSide) + } else if (it instanceof GString) { + return ContentUtils.extractValue(it , null, { + if (it instanceof DslProperty) { + return clientSide ? + getClientOrServerSideValues((it as DslProperty).clientValue, clientSide) : getClientOrServerSideValues((it as DslProperty).serverValue, clientSide) + } + return it + }) + } + return it + } + } } diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodBuilderSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodBuilderSpec.groovy index 8bd272b7ac..f2ae54c6cf 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodBuilderSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodBuilderSpec.groovy @@ -1,10 +1,12 @@ package io.codearte.accurest.builder import io.codearte.accurest.dsl.GroovyDsl +import io.codearte.accurest.dsl.WireMockStubStrategy +import io.codearte.accurest.dsl.WireMockStubVerifier import spock.lang.Issue import spock.lang.Specification -class JaxRsClientSpockMethodBuilderSpec extends Specification { +class JaxRsClientSpockMethodBuilderSpec extends Specification implements WireMockStubVerifier { def "should generate assertions for simple response body"() { given: @@ -28,6 +30,8 @@ class JaxRsClientSpockMethodBuilderSpec extends Specification { then: blockBuilder.toString().contains("\$[?(@.property1 == 'a')]") blockBuilder.toString().contains("\$[?(@.property2 == 'b')]") + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) } @Issue("#79") @@ -57,6 +61,8 @@ class JaxRsClientSpockMethodBuilderSpec extends Specification { blockBuilder.toString().contains("\$[?(@.property1 == 'a')]") blockBuilder.toString().contains("\$.property2[*][?(@.a == 'sth')]") blockBuilder.toString().contains("\$.property2[*][?(@.b == 'sthElse')]") + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) } @Issue("#82") @@ -80,6 +86,8 @@ class JaxRsClientSpockMethodBuilderSpec extends Specification { builder.appendTo(blockBuilder) then: blockBuilder.toString().contains("entity('{\"items\":[\"HOP\"]}', 'application/json')") + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) } @Issue("#88") @@ -103,6 +111,8 @@ class JaxRsClientSpockMethodBuilderSpec extends Specification { builder.appendTo(blockBuilder) then: blockBuilder.toString().contains("entity('property1=VAL1', 'application/octet-stream')") + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) } def "should generate assertions for array in response body"() { @@ -130,6 +140,8 @@ class JaxRsClientSpockMethodBuilderSpec extends Specification { then: blockBuilder.toString().contains("\$[*][?(@.property1 == 'a')]") blockBuilder.toString().contains("\$[*][?(@.property2 == 'b')]") + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) } def "should generate assertions for array inside response body element"() { @@ -156,6 +168,8 @@ class JaxRsClientSpockMethodBuilderSpec extends Specification { then: blockBuilder.toString().contains("\$.property1[*][?(@.property3 == 'test2')]") blockBuilder.toString().contains("\$.property1[*][?(@.property2 == 'test1')]") + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) } def "should generate assertions for nested objects in response body"() { @@ -182,6 +196,8 @@ class JaxRsClientSpockMethodBuilderSpec extends Specification { then: blockBuilder.toString().contains("\$.property2[?(@.property3 == 'b')]") blockBuilder.toString().contains("\$[?(@.property1 == 'a')]") + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) } def "should generate regex assertions for map objects in response body"() { @@ -214,6 +230,8 @@ class JaxRsClientSpockMethodBuilderSpec extends Specification { then: blockBuilder.toString().contains("\$[?(@.property2 =~ /[0-9]{3}/)]") blockBuilder.toString().contains("\$[?(@.property1 == 'a')]") + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) } def "should generate regex assertions for string objects in response body"() { @@ -240,6 +258,8 @@ class JaxRsClientSpockMethodBuilderSpec extends Specification { then: blockBuilder.toString().contains("\$[?(@.property2 =~ /[0-9]{3}/)]") blockBuilder.toString().contains("\$[?(@.property1 == 'a')]") + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) } def "should ignore 'Accept' header and use 'request' method"() { @@ -262,6 +282,8 @@ class JaxRsClientSpockMethodBuilderSpec extends Specification { builder.appendTo(blockBuilder) then: blockBuilder.toString().contains("request('text/plain')") + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) } def "should ignore 'Content-Type' header and use 'entity' method"() { @@ -288,7 +310,8 @@ class JaxRsClientSpockMethodBuilderSpec extends Specification { blockBuilder.toString().contains("entity('', 'text/plain')") blockBuilder.toString().contains("header('Timer', '123')") !blockBuilder.toString().contains("header('Content-Type'") - + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) } def "should generate a call with an url path and query parameters"() { @@ -337,6 +360,8 @@ class JaxRsClientSpockMethodBuilderSpec extends Specification { spockTest.contains("queryParam('email', 'bob@email.com'") spockTest.contains('$[?(@.property2 == \'b\')]') spockTest.contains('$[?(@.property1 == \'a\')]') + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) } def "should generate test for empty body"() { @@ -358,6 +383,8 @@ class JaxRsClientSpockMethodBuilderSpec extends Specification { def spockTest = blockBuilder.toString() then: spockTest.contains("entity('', 'application/octet-stream')") + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) } def "should generate test for String in response body"() { @@ -380,6 +407,8 @@ class JaxRsClientSpockMethodBuilderSpec extends Specification { then: spockTest.contains('def responseBody = (response.body.asString())') spockTest.contains('responseBody == "test"') + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) } } diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy index 5d25d94874..a56df6014b 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy @@ -1,13 +1,15 @@ package io.codearte.accurest.builder import io.codearte.accurest.dsl.GroovyDsl +import io.codearte.accurest.dsl.WireMockStubStrategy +import io.codearte.accurest.dsl.WireMockStubVerifier import spock.lang.Issue import spock.lang.Specification /** * @author Jakub Kubrynski */ -class MockMvcSpockMethodBuilderSpec extends Specification { +class MockMvcSpockMethodBuilderSpec extends Specification implements WireMockStubVerifier { def "should generate assertions for simple response body"() { given: @@ -31,6 +33,8 @@ class MockMvcSpockMethodBuilderSpec extends Specification { then: blockBuilder.toString().contains("\$[?(@.property1 == 'a')]") blockBuilder.toString().contains("\$[?(@.property2 == 'b')]") + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) } @Issue("#79") @@ -60,6 +64,8 @@ class MockMvcSpockMethodBuilderSpec extends Specification { blockBuilder.toString().contains("\$[?(@.property1 == 'a')]") blockBuilder.toString().contains("\$.property2[*][?(@.a == 'sth')]") blockBuilder.toString().contains("\$.property2[*][?(@.b == 'sthElse')]") + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) } @Issue("#82") @@ -83,6 +89,8 @@ class MockMvcSpockMethodBuilderSpec extends Specification { builder.appendTo(blockBuilder) then: blockBuilder.toString().contains(".body('{\"items\":[\"HOP\"]}')") + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) } @Issue("#88") @@ -106,6 +114,8 @@ class MockMvcSpockMethodBuilderSpec extends Specification { builder.appendTo(blockBuilder) then: blockBuilder.toString().contains(".body('property1=VAL1')") + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) } def "should generate assertions for array in response body"() { @@ -133,6 +143,8 @@ class MockMvcSpockMethodBuilderSpec extends Specification { then: blockBuilder.toString().contains("\$[*][?(@.property1 == 'a')]") blockBuilder.toString().contains("\$[*][?(@.property2 == 'b')]") + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) } def "should generate assertions for array inside response body element"() { @@ -159,6 +171,8 @@ class MockMvcSpockMethodBuilderSpec extends Specification { then: blockBuilder.toString().contains("\$.property1[*][?(@.property3 == 'test2')]") blockBuilder.toString().contains("\$.property1[*][?(@.property2 == 'test1')]") + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) } def "should generate assertions for nested objects in response body"() { @@ -185,6 +199,8 @@ class MockMvcSpockMethodBuilderSpec extends Specification { then: blockBuilder.toString().contains("\$.property2[?(@.property3 == 'b')]") blockBuilder.toString().contains("\$[?(@.property1 == 'a')]") + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) } def "should generate regex assertions for map objects in response body"() { @@ -217,6 +233,8 @@ class MockMvcSpockMethodBuilderSpec extends Specification { then: blockBuilder.toString().contains("\$[?(@.property2 =~ /[0-9]{3}/)]") blockBuilder.toString().contains("\$[?(@.property1 == 'a')]") + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) } def "should generate regex assertions for string objects in response body"() { @@ -243,6 +261,8 @@ class MockMvcSpockMethodBuilderSpec extends Specification { then: blockBuilder.toString().contains("\$[?(@.property2 =~ /[0-9]{3}/)]") blockBuilder.toString().contains("\$[?(@.property1 == 'a')]") + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) } def "should generate a call with an url path and query parameters"() { @@ -284,6 +304,8 @@ class MockMvcSpockMethodBuilderSpec extends Specification { spockTest.contains('get("/users?limit=10&offset=20&filter=email&sort=name&search=55&age=99&name=Denis.Stepanov&email=bob@email.com")') spockTest.contains('$[?(@.property2 == \'b\')]') spockTest.contains('$[?(@.property1 == \'a\')]') + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) } def "should generate test for empty body"() { @@ -305,6 +327,8 @@ class MockMvcSpockMethodBuilderSpec extends Specification { def spockTest = blockBuilder.toString() then: spockTest.contains(".body('')") + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) } def "should generate test for String in response body"() { @@ -327,6 +351,8 @@ class MockMvcSpockMethodBuilderSpec extends Specification { then: spockTest.contains('def responseBody = (response.body.asString())') spockTest.contains('responseBody == "test"') + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) } @Issue('113') @@ -360,6 +386,8 @@ class MockMvcSpockMethodBuilderSpec extends Specification { def spockTest = blockBuilder.toString() then: spockTest.contains('''response.header('Location') ==~ java.util.regex.Pattern.compile('http://localhost/partners/[0-9]+/users/[0-9]+')''') + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) } @Issue('115') @@ -393,6 +421,8 @@ class MockMvcSpockMethodBuilderSpec extends Specification { def spockTest = blockBuilder.toString() then: spockTest.contains('''response.header('Location') ==~ java.util.regex.Pattern.compile('^((http[s]?|ftp):\\/)\\/?([^:\\/\\s]+)(:[0-9]{1,5})?/partners/[0-9]+/users/[0-9]+')''') + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) } def "should work with more complex stuff and jsonpaths"() { @@ -428,6 +458,37 @@ class MockMvcSpockMethodBuilderSpec extends Specification { then: spockTest.contains('''$.errors[*][?(@.property == 'bank_account_number')]''') spockTest.contains('''$.errors[*][?(@.message == 'incorrect_format')]''') + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) + } + + def "should work properly with GString url"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + + request { + method 'PUT' + url "/partners/${value(client(regex('^[0-9]*$')), server('11'))}/agents/11/customers/09665703Z" + headers { + header 'Content-Type': 'application/json' + } + body( + first_name: 'Josef', + ) + } + response { + status 422 + } + } + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + def spockTest = blockBuilder.toString() + then: + spockTest.contains('''/partners/11/agents/11/customers/09665703Z''') + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) } def "should resolve properties in GString with regular expression"() { diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy index 548f309b00..2188321374 100755 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy @@ -4,8 +4,9 @@ import groovy.json.JsonBuilder import groovy.json.JsonSlurper import io.codearte.accurest.util.AssertionUtil import spock.lang.Issue +import spock.lang.Specification -class WireMockGroovyDslSpec extends WireMockSpec { +class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifier { def 'should convert groovy dsl stub to wireMock stub for the client side'() { given: @@ -1189,6 +1190,109 @@ class WireMockGroovyDslSpec extends WireMockSpec { stubMappingIsValidWireMockStub(wireMockStub) } + def 'should generate stub properly resolving GString with regular expression'() { + given: + GroovyDsl groovyDsl = GroovyDsl.make { + priority 1 + request { + method 'POST' + url '/users/password' + headers { + header 'Content-Type': 'application/json' + } + body( + email: $(client(regex(email())), server('not.existing@user.com')), + callback_url: $(client(regex(hostname())), server('http://partners.com')) + ) + } + response { + status 404 + headers { + header 'Content-Type': 'application/json' + } + body( + code: 4, + 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})?/)]" + }, { + "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\\":4,\\"message\\":\\"User not found by email = [not.existing@user.com]\\"}", + "headers" : { + "Content-Type" : "application/json" + } + }, + "priority" : 1 + } + '''), wireMockStub) + and: + stubMappingIsValidWireMockStub(wireMockStub) + } + + def 'should generate stub properly resolving GString with regular expression in url'() { + given: + GroovyDsl groovyDsl = GroovyDsl.make { + + request { + method 'PUT' + url "/partners/${value(client(regex('^[0-9]*$')), server('11'))}/agents/11/customers/09665703Z" + headers { + header 'Content-Type': 'application/json' + } + body( + first_name: 'Josef', + ) + } + response { + status 422 + } + } + when: + String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() + then: + AssertionUtil.assertThatJsonsAreEqual((''' + { + "request" : { + "urlPattern" : "/partners/^[0-9]*$/agents/11/customers/09665703Z", + "method" : "PUT", + "bodyPatterns" : [ { + "matchesJsonPath" : "$[?(@.first_name == 'Josef')]" + } ], + "headers" : { + "Content-Type" : { + "equalTo" : "application/json" + } + } + }, + "response" : { + "status" : 422 + } + } + '''), wireMockStub) + and: + stubMappingIsValidWireMockStub(wireMockStub) + } + String toJsonString(value) { new JsonBuilder(value).toPrettyString() } diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockStubVerifier.groovy similarity index 82% rename from accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockSpec.groovy rename to accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockStubVerifier.groovy index aad0fa879e..a970434c05 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockStubVerifier.groovy @@ -1,17 +1,16 @@ package io.codearte.accurest.dsl - import com.github.tomakehurst.wiremock.stubbing.StubMapping -import spock.lang.Specification import java.util.regex.Pattern -abstract class WireMockSpec extends Specification { +trait WireMockStubVerifier { void stubMappingIsValidWireMockStub(String mappingDefinition) { StubMapping stubMapping = StubMapping.buildFrom(mappingDefinition) stubMapping.request.bodyPatterns.findAll { it.matches }.every { Pattern.compile(it.matches) } + assert !mappingDefinition.contains('DslProperty') } } From 5181608ee9ac0613c0e66116175b2b19cddbb2f8 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Thu, 3 Sep 2015 17:20:48 +0200 Subject: [PATCH 087/119] Added tabs instead of spaces --- .../DslToWireMockClientConverterSpec.groovy | 118 ++--- .../io/codearte/accurest/TestGenerator.groovy | 8 +- .../JaxRsClientSpockMethodBodyBuilder.groovy | 136 +++--- .../MockMvcSpockMethodBodyBuilder.groovy | 114 ++--- .../dsl/internal/ExecutionProperty.groovy | 16 +- .../accurest/dsl/internal/Header.groovy | 18 +- .../dsl/internal/QueryParameter.groovy | 32 +- .../dsl/internal/QueryParameters.groovy | 16 +- .../accurest/dsl/internal/UrlPath.groovy | 12 +- .../codearte/accurest/util/ContentType.groovy | 14 +- .../accurest/util/ValidateUtils.groovy | 48 +- .../JaxRsClientSpockMethodBuilderSpec.groovy | 20 +- .../MockMvcSpockMethodBuilderSpec.groovy | 20 +- .../accurest/dsl/WireMockGroovyDslSpec.groovy | 434 +++++++++--------- .../accurest/dsl/WireMockStubVerifier.groovy | 14 +- .../dsl/internal/ExecutionPropertySpec.groovy | 22 +- .../test/resources/dsl/basic/sampleDsl.groovy | 20 +- .../functionalTest/bootSimple/build.gradle | 80 ++-- .../pairId/colleratePlacesFromTweet.groovy | 28 +- .../pairId/moreComplexVersion.groovy | 36 +- .../ofg/twitter/place/PairIdController.groovy | 34 +- .../groovy/com/ofg/twitter/place/Tweet.java | 14 +- .../ofg/twitter/places/AcceptanceSpec.groovy | 18 +- .../ofg/twitter/places/BaseMockMvcSpec.groovy | 12 +- .../shouldMarkClientAsFraud.groovy | 48 +- .../shouldMarkClientAsNotFraud.groovy | 52 +-- .../frauddetection/FraudRestApplication.java | 8 +- .../shouldMarkClientAsFraud.json | 42 +- .../shouldMarkClientAsNotFraud.json | 42 +- .../shouldMarkClientAsFraud.groovy | 48 +- .../shouldMarkClientAsNotFraud.groovy | 52 +-- .../shouldMarkClientAsFraud.json | 42 +- .../shouldMarkClientAsNotFraud.json | 42 +- build.gradle | 3 +- 34 files changed, 831 insertions(+), 832 deletions(-) diff --git a/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/DslToWireMockClientConverterSpec.groovy b/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/DslToWireMockClientConverterSpec.groovy index 22fc850efe..61c6b7c29b 100755 --- a/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/DslToWireMockClientConverterSpec.groovy +++ b/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/DslToWireMockClientConverterSpec.groovy @@ -10,15 +10,15 @@ class DslToWireMockClientConverterSpec extends Specification { def converter = new DslToWireMockClientConverter() and: String dslBody = """ - io.codearte.accurest.dsl.GroovyDsl.make { - request { - method('PUT') - url \$(client(~/\\/[0-9]{2}/), server('/12')) - } - response { - status 200 - } - } + io.codearte.accurest.dsl.GroovyDsl.make { + request { + method('PUT') + url \$(client(~/\\/[0-9]{2}/), server('/12')) + } + response { + status 200 + } + } """ when: String json = converter.convertContent(dslBody) @@ -34,7 +34,7 @@ class DslToWireMockClientConverterSpec extends Specification { def converter = new DslToWireMockClientConverter() and: String dslBody = """ - io.codearte.accurest.dsl.GroovyDsl.make { + io.codearte.accurest.dsl.GroovyDsl.make { request { method 'PUT' url '/api/12' @@ -84,57 +84,57 @@ class DslToWireMockClientConverterSpec extends Specification { JSONAssert.assertEquals(''' { "request" : { - "url" : "/api/12", - "method" : "PUT", - "bodyPatterns" : [ { - "matchesJsonPath" : "$[*].place.bounding_box.coordinates[*][*][?(@ == 38.995548)]" - }, { - "matchesJsonPath" : "$[*].place[?(@.country == 'United States')]" - }, { - "matchesJsonPath" : "$[*].place.bounding_box.coordinates[*][*][?(@ == -77.119759)]" - }, { - "matchesJsonPath" : "$[*].place[?(@.name == 'Washington')]" - }, { - "matchesJsonPath" : "$[*].place.bounding_box[?(@.type == 'Polygon')]" - }, { - "matchesJsonPath" : "$[*][?(@.id_str == '492967299297845248')]" - }, { - "matchesJsonPath" : "$[*].place[?(@.country_code == 'US')]" - }, { - "matchesJsonPath" : "$[*][?(@.id == 492967299297845248)]" - }, { - "matchesJsonPath" : "$[*].place.bounding_box.coordinates[*][*][?(@ == -76.909393)]" - }, { - "matchesJsonPath" : "$[*].place.bounding_box.coordinates[*][*][?(@ == 38.791645)]" - }, { - "matchesJsonPath" : "$[*].place[?(@.id == '01fbe706f872cb32')]" - }, { - "matchesJsonPath" : "$[*].place[?(@.url == 'http://api.twitter.com/1/geo/id/01fbe706f872cb32.json')]" - }, { - "matchesJsonPath" : "$[*].place.bounding_box.coordinates[*][*][?(@ == -77.119759)]" - }, { - "matchesJsonPath" : "$[*].place.bounding_box.coordinates[*][*][?(@ == -76.909393)]" - }, { - "matchesJsonPath" : "$[*].place.bounding_box.coordinates[*][*][?(@ == 38.995548)]" - }, { - "matchesJsonPath" : "$[*][?(@.text == 'Gonna see you at Warsaw')]" - }, { - "matchesJsonPath" : "$[*].place[?(@.place_type == 'city')]" - }, { - "matchesJsonPath" : "$[*][?(@.created_at == 'Sat Jul 26 09:38:57 +0000 2014')]" - }, { - "matchesJsonPath" : "$[*].place[?(@.full_name == 'Washington, DC')]" - }, { - "matchesJsonPath" : "$[*].place.bounding_box.coordinates[*][*][?(@ == 38.791645)]" - } ], - "headers" : { - "Content-Type" : { - "equalTo" : "application/vnd.com.ofg.twitter-places-analyzer.v1+json" - } - } + "url" : "/api/12", + "method" : "PUT", + "bodyPatterns" : [ { + "matchesJsonPath" : "$[*].place.bounding_box.coordinates[*][*][?(@ == 38.995548)]" + }, { + "matchesJsonPath" : "$[*].place[?(@.country == 'United States')]" + }, { + "matchesJsonPath" : "$[*].place.bounding_box.coordinates[*][*][?(@ == -77.119759)]" + }, { + "matchesJsonPath" : "$[*].place[?(@.name == 'Washington')]" + }, { + "matchesJsonPath" : "$[*].place.bounding_box[?(@.type == 'Polygon')]" + }, { + "matchesJsonPath" : "$[*][?(@.id_str == '492967299297845248')]" + }, { + "matchesJsonPath" : "$[*].place[?(@.country_code == 'US')]" + }, { + "matchesJsonPath" : "$[*][?(@.id == 492967299297845248)]" + }, { + "matchesJsonPath" : "$[*].place.bounding_box.coordinates[*][*][?(@ == -76.909393)]" + }, { + "matchesJsonPath" : "$[*].place.bounding_box.coordinates[*][*][?(@ == 38.791645)]" + }, { + "matchesJsonPath" : "$[*].place[?(@.id == '01fbe706f872cb32')]" + }, { + "matchesJsonPath" : "$[*].place[?(@.url == 'http://api.twitter.com/1/geo/id/01fbe706f872cb32.json')]" + }, { + "matchesJsonPath" : "$[*].place.bounding_box.coordinates[*][*][?(@ == -77.119759)]" + }, { + "matchesJsonPath" : "$[*].place.bounding_box.coordinates[*][*][?(@ == -76.909393)]" + }, { + "matchesJsonPath" : "$[*].place.bounding_box.coordinates[*][*][?(@ == 38.995548)]" + }, { + "matchesJsonPath" : "$[*][?(@.text == 'Gonna see you at Warsaw')]" + }, { + "matchesJsonPath" : "$[*].place[?(@.place_type == 'city')]" + }, { + "matchesJsonPath" : "$[*][?(@.created_at == 'Sat Jul 26 09:38:57 +0000 2014')]" + }, { + "matchesJsonPath" : "$[*].place[?(@.full_name == 'Washington, DC')]" + }, { + "matchesJsonPath" : "$[*].place.bounding_box.coordinates[*][*][?(@ == 38.791645)]" + } ], + "headers" : { + "Content-Type" : { + "equalTo" : "application/vnd.com.ofg.twitter-places-analyzer.v1+json" + } + } }, "response" : { - "status" : 200 + "status" : 200 } } ''', json, false) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/TestGenerator.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/TestGenerator.groovy index fb612cebd1..63e2047217 100755 --- a/accurest-core/src/main/groovy/io/codearte/accurest/TestGenerator.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/TestGenerator.groovy @@ -68,8 +68,8 @@ class TestGenerator { } } } - - private String normalizePath(String path) { - return FilenameUtils.separatorsToUnix(path) - } + + private String normalizePath(String path) { + return FilenameUtils.separatorsToUnix(path) + } } \ No newline at end of file diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodBodyBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodBodyBuilder.groovy index d3e07d7942..91ba58e4f0 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodBodyBuilder.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodBodyBuilder.groovy @@ -10,86 +10,86 @@ import io.codearte.accurest.dsl.internal.QueryParameter @TypeChecked class JaxRsClientSpockMethodBodyBuilder extends SpockMethodBodyBuilder { - JaxRsClientSpockMethodBodyBuilder(GroovyDsl stubDefinition) { - super(stubDefinition) - } + JaxRsClientSpockMethodBodyBuilder(GroovyDsl stubDefinition) { + super(stubDefinition) + } - @Override - protected void givenBlock(BlockBuilder bb) { - } + @Override + protected void givenBlock(BlockBuilder bb) { + } - @Override - protected void when(BlockBuilder bb) { - bb.addLine("def response = webTarget") - bb.indent() + @Override + protected void when(BlockBuilder bb) { + bb.addLine("def response = webTarget") + bb.indent() - appendUrlPathAndQueryParameters(bb) - appendRequestWithRequiredResponseContentType(bb) - appendHeaders(bb) - appendMethodAndBody(bb) + appendUrlPathAndQueryParameters(bb) + appendRequestWithRequiredResponseContentType(bb) + appendHeaders(bb) + appendMethodAndBody(bb) - bb.unindent() + bb.unindent() - bb.addEmptyLine() - bb.addLine("String responseAsString = response.readEntity(String)") - } + bb.addEmptyLine() + bb.addLine("String responseAsString = response.readEntity(String)") + } - protected void appendRequestWithRequiredResponseContentType(BlockBuilder bb) { - String acceptHeader = getHeader("Accept") - if (acceptHeader) { - bb.addLine(".request('$acceptHeader')") - } else { - bb.addLine(".request()") - } - } + protected void appendRequestWithRequiredResponseContentType(BlockBuilder bb) { + String acceptHeader = getHeader("Accept") + if (acceptHeader) { + bb.addLine(".request('$acceptHeader')") + } else { + bb.addLine(".request()") + } + } - protected void appendUrlPathAndQueryParameters(BlockBuilder bb) { - if (request.url) { - bb.addLine(".path('$request.url.serverValue')") - } else if (request.urlPath) { - bb.addLine(".path('$request.urlPath.serverValue')") - request.urlPath.queryParameters?.parameters.findAll(this.&allowedQueryParameter).each { QueryParameter param -> - bb.addLine(".queryParam('$param.name', '${resolveParamValue(param).toString()}')") - } - } - } + protected void appendUrlPathAndQueryParameters(BlockBuilder bb) { + if (request.url) { + bb.addLine(".path('$request.url.serverValue')") + } else if (request.urlPath) { + bb.addLine(".path('$request.urlPath.serverValue')") + request.urlPath.queryParameters?.parameters.findAll(this.&allowedQueryParameter).each { QueryParameter param -> + bb.addLine(".queryParam('$param.name', '${resolveParamValue(param).toString()}')") + } + } + } - protected void appendMethodAndBody(BlockBuilder bb) { - String method = request.method.serverValue.toString().toLowerCase() - if (request.body) { - String contentType = getHeader('Content-Type') ?: getRequestContentType().mimeType - bb.addLine(".method('$method', entity('$bodyAsString', '$contentType'))") - } else { - bb.addLine(".method('$method')") - } - } + protected void appendMethodAndBody(BlockBuilder bb) { + String method = request.method.serverValue.toString().toLowerCase() + if (request.body) { + String contentType = getHeader('Content-Type') ?: getRequestContentType().mimeType + bb.addLine(".method('$method', entity('$bodyAsString', '$contentType'))") + } else { + bb.addLine(".method('$method')") + } + } - protected appendHeaders(BlockBuilder bb) { - request.headers?.collect { Header header -> - if (header.name == 'Content-Type' || header.name == 'Accept') return // Particular headers are set via 'request' / 'entity' methods - bb.addLine(".header('${header.name}', '${header.serverValue}')") - } - } + protected appendHeaders(BlockBuilder bb) { + request.headers?.collect { Header header -> + if (header.name == 'Content-Type' || header.name == 'Accept') return // Particular headers are set via 'request' / 'entity' methods + bb.addLine(".header('${header.name}', '${header.serverValue}')") + } + } - protected String getHeader(String name) { - return request.headers?.entries.find { it.name == name }?.serverValue - } + protected String getHeader(String name) { + return request.headers?.entries.find { it.name == name }?.serverValue + } - @Override - protected void validateResponseCodeBlock(BlockBuilder bb) { - bb.addLine("response.status == $response.status.serverValue") - } + @Override + protected void validateResponseCodeBlock(BlockBuilder bb) { + bb.addLine("response.status == $response.status.serverValue") + } - @Override - protected void validateResponseHeadersBlock(BlockBuilder bb) { - response.headers?.collect { Header header -> - bb.addLine("response.getHeaderString('$header.name') == '$header.serverValue'") - } - } + @Override + protected void validateResponseHeadersBlock(BlockBuilder bb) { + response.headers?.collect { Header header -> + bb.addLine("response.getHeaderString('$header.name') == '$header.serverValue'") + } + } - @Override - protected String getResponseAsString() { - return 'responseAsString' - } + @Override + protected String getResponseAsString() { + return 'responseAsString' + } } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBodyBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBodyBuilder.groovy index 1b2e9a9d24..fed69dc27b 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBodyBuilder.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBodyBuilder.groovy @@ -14,73 +14,73 @@ import java.util.regex.Pattern @TypeChecked class MockMvcSpockMethodBodyBuilder extends SpockMethodBodyBuilder { - MockMvcSpockMethodBodyBuilder(GroovyDsl stubDefinition) { - super(stubDefinition) - } + MockMvcSpockMethodBodyBuilder(GroovyDsl stubDefinition) { + super(stubDefinition) + } - protected void given(BlockBuilder bb) { - bb.addLine('def request = given()') - bb.indent() - request.headers?.collect { Header header -> - bb.addLine(".header('${getTestSideValue(header.name)}', '${getTestSideValue(header.serverValue)}')") - } - if (request.body) { - bb.addLine(".body('$bodyAsString')") - } - bb.unindent() - } + protected void given(BlockBuilder bb) { + bb.addLine('def request = given()') + bb.indent() + request.headers?.collect { Header header -> + bb.addLine(".header('${getTestSideValue(header.name)}', '${getTestSideValue(header.serverValue)}')") + } + if (request.body) { + bb.addLine(".body('$bodyAsString')") + } + bb.unindent() + } - protected void when(BlockBuilder bb) { - bb.addLine('def response = given().spec(request)') - bb.indent() + protected void when(BlockBuilder bb) { + bb.addLine('def response = given().spec(request)') + bb.indent() - String url = buildUrl(request) - String method = request.method.serverValue.toString().toLowerCase() + String url = buildUrl(request) + String method = request.method.serverValue.toString().toLowerCase() - bb.addLine(/.${method}("$url")/) - bb.unindent() - } + bb.addLine(/.${method}("$url")/) + bb.unindent() + } - protected void validateResponseCodeBlock(BlockBuilder bb) { - bb.addLine("response.statusCode == $response.status.serverValue") - } + protected void validateResponseCodeBlock(BlockBuilder bb) { + bb.addLine("response.statusCode == $response.status.serverValue") + } - protected void validateResponseHeadersBlock(BlockBuilder bb) { - response.headers?.collect { Header header -> - bb.addLine("response.header('$header.name') ${convertHeaderComparison(header.serverValue)}") - } - } + protected void validateResponseHeadersBlock(BlockBuilder bb) { + response.headers?.collect { Header header -> + bb.addLine("response.header('$header.name') ${convertHeaderComparison(header.serverValue)}") + } + } - private String convertHeaderComparison(Object headerValue) { - return " == '$headerValue'" - } + private String convertHeaderComparison(Object headerValue) { + return " == '$headerValue'" + } - private String convertHeaderComparison(Pattern headerValue) { - return "==~ java.util.regex.Pattern.compile('$headerValue')" - } + private String convertHeaderComparison(Pattern headerValue) { + return "==~ java.util.regex.Pattern.compile('$headerValue')" + } - @Override - protected String getResponseAsString() { - return 'response.body.asString()' - } + @Override + protected String getResponseAsString() { + return 'response.body.asString()' + } - protected String buildUrl(Request request) { - if (request.url) - return getTestSideValue(request.url.serverValue) - if (request.urlPath) - return getTestSideValue(buildUrlFromUrlPath(request.urlPath)) - throw new IllegalStateException("URL is not set!") - } + protected String buildUrl(Request request) { + if (request.url) + return getTestSideValue(request.url.serverValue) + if (request.urlPath) + return getTestSideValue(buildUrlFromUrlPath(request.urlPath)) + throw new IllegalStateException("URL is not set!") + } - @TypeChecked(TypeCheckingMode.SKIP) - protected String buildUrlFromUrlPath(UrlPath urlPath) { - String params = urlPath.queryParameters.parameters - .findAll(this.&allowedQueryParameter) - .inject([] as List) { List result, QueryParameter param -> - result << "${param.name}=${resolveParamValue(param).toString()}" - } - .join('&') - return "$urlPath.serverValue?$params" - } + @TypeChecked(TypeCheckingMode.SKIP) + protected String buildUrlFromUrlPath(UrlPath urlPath) { + String params = urlPath.queryParameters.parameters + .findAll(this.&allowedQueryParameter) + .inject([] as List) { List result, QueryParameter param -> + result << "${param.name}=${resolveParamValue(param).toString()}" + } + .join('&') + return "$urlPath.serverValue?$params" + } } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/ExecutionProperty.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/ExecutionProperty.groovy index ccc7037575..866afdc18c 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/ExecutionProperty.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/ExecutionProperty.groovy @@ -5,15 +5,15 @@ import groovy.transform.CompileStatic @CompileStatic class ExecutionProperty { - private static final String PLACEHOLDER_VALUE = '\\$it' + private static final String PLACEHOLDER_VALUE = '\\$it' - final String executionCommand + final String executionCommand - ExecutionProperty(String executionCommand) { - this.executionCommand = executionCommand - } + ExecutionProperty(String executionCommand) { + this.executionCommand = executionCommand + } - String insertValue(String valueToInsert) { - return executionCommand.replaceAll(PLACEHOLDER_VALUE, valueToInsert) - } + String insertValue(String valueToInsert) { + return executionCommand.replaceAll(PLACEHOLDER_VALUE, valueToInsert) + } } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Header.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Header.groovy index a7eefa180a..b72037cd03 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Header.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Header.groovy @@ -8,16 +8,16 @@ import groovy.transform.ToString @CompileStatic class Header extends DslProperty { - String name + String name - Header(String name, DslProperty dslProperty) { - super(dslProperty.clientValue, dslProperty.serverValue) - this.name = name - } + Header(String name, DslProperty dslProperty) { + super(dslProperty.clientValue, dslProperty.serverValue) + this.name = name + } - Header(String name, Object value) { - super(value) - this.name = name - } + Header(String name, Object value) { + super(value) + this.name = name + } } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameter.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameter.groovy index 042da081f2..153fa6f297 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameter.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameter.groovy @@ -11,24 +11,24 @@ import static io.codearte.accurest.util.ValidateUtils.validateServerValueIsAvail @CompileStatic class QueryParameter extends DslProperty { - String name + String name - QueryParameter(String name, DslProperty dslProperty) { - super(dslProperty.clientValue, dslProperty.serverValue) - validateServerValueIsAvailable(dslProperty.serverValue, "Query parameter '$name'") - this.name = name - } + QueryParameter(String name, DslProperty dslProperty) { + super(dslProperty.clientValue, dslProperty.serverValue) + validateServerValueIsAvailable(dslProperty.serverValue, "Query parameter '$name'") + this.name = name + } - QueryParameter(String name, MatchingStrategy matchingStrategy) { - super(matchingStrategy) - validateServerValueIsAvailable(matchingStrategy, "Query parameter '$name'") - this.name = name - } + QueryParameter(String name, MatchingStrategy matchingStrategy) { + super(matchingStrategy) + validateServerValueIsAvailable(matchingStrategy, "Query parameter '$name'") + this.name = name + } - QueryParameter(String name, Object value) { - super(value) - validateServerValueIsAvailable(value, "Query parameter '$name'") - this.name = name - } + QueryParameter(String name, Object value) { + super(value) + validateServerValueIsAvailable(value, "Query parameter '$name'") + this.name = name + } } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameters.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameters.groovy index 0a5d6233f2..b4cc1d74c4 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameters.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameters.groovy @@ -9,15 +9,15 @@ import groovy.transform.TypeChecked @TypeChecked class QueryParameters { - List parameters = [] + List parameters = [] - void parameter(Map singleParameter) { - Map.Entry first = singleParameter.entrySet().first() - parameters << new QueryParameter(first?.key, first?.value) - } + void parameter(Map singleParameter) { + Map.Entry first = singleParameter.entrySet().first() + parameters << new QueryParameter(first?.key, first?.value) + } - void parameter(String parameterName, Object parameterValue) { - parameters << new QueryParameter(parameterName, parameterValue) - } + void parameter(String parameterName, Object parameterValue) { + parameters << new QueryParameter(parameterName, parameterValue) + } } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/UrlPath.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/UrlPath.groovy index ee1d3eed98..68d9bc0f7d 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/UrlPath.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/UrlPath.groovy @@ -9,12 +9,12 @@ import groovy.transform.ToString; @CompileStatic class UrlPath extends Url { - UrlPath(String path) { - super(path) - } + UrlPath(String path) { + super(path) + } - UrlPath(DslProperty path) { - super(path) - } + UrlPath(DslProperty path) { + super(path) + } } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentType.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentType.groovy index 7b9e402985..a9243058af 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentType.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentType.groovy @@ -2,14 +2,14 @@ package io.codearte.accurest.util enum ContentType { - JSON("application/json"), - XML("application/xml"), - UNKNOWN("application/octet-stream") + JSON("application/json"), + XML("application/xml"), + UNKNOWN("application/octet-stream") - final String mimeType + final String mimeType - ContentType(String mimeType) { - this.mimeType = mimeType - } + ContentType(String mimeType) { + this.mimeType = mimeType + } } \ No newline at end of file diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/util/ValidateUtils.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/ValidateUtils.groovy index 7cb6e0f0ef..46e9706cfe 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/util/ValidateUtils.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/ValidateUtils.groovy @@ -12,35 +12,35 @@ import static io.codearte.accurest.dsl.internal.MatchingStrategy.Type.EQUAL_TO @TypeChecked class ValidateUtils { - static Object validateServerValueIsAvailable(Object value) { - validateServerValueIsAvailable(value, "Server value") - return value - } + static Object validateServerValueIsAvailable(Object value) { + validateServerValueIsAvailable(value, "Server value") + return value + } - static Object validateServerValueIsAvailable(Object value, String msg) { - validateServerValue(value, msg) - return value - } + static Object validateServerValueIsAvailable(Object value, String msg) { + validateServerValue(value, msg) + return value + } - static void validateServerValue(Pattern pattern, String msg) { - throw new IllegalStateException("$msg can't be a pattern for the server side") - } + static void validateServerValue(Pattern pattern, String msg) { + throw new IllegalStateException("$msg can't be a pattern for the server side") + } - static List ALLOWED_MATCHING_TYPES_ON_SERVER_SIDE = [EQUAL_TO, ABSENT] + static List ALLOWED_MATCHING_TYPES_ON_SERVER_SIDE = [EQUAL_TO, ABSENT] - static void validateServerValue(MatchingStrategy matchingStrategy, String msg) { - if (!ALLOWED_MATCHING_TYPES_ON_SERVER_SIDE.contains(matchingStrategy.type)) { - throw new IllegalStateException("$msg can't be of a matching type: $matchingStrategy.type for the server side") - } - validateServerValue(matchingStrategy.serverValue, msg) - } + static void validateServerValue(MatchingStrategy matchingStrategy, String msg) { + if (!ALLOWED_MATCHING_TYPES_ON_SERVER_SIDE.contains(matchingStrategy.type)) { + throw new IllegalStateException("$msg can't be of a matching type: $matchingStrategy.type for the server side") + } + validateServerValue(matchingStrategy.serverValue, msg) + } - static void validateServerValue(DslProperty value, String msg) { - validateServerValue(value.serverValue, msg) - } + static void validateServerValue(DslProperty value, String msg) { + validateServerValue(value.serverValue, msg) + } - static void validateServerValue(Object value, String msg) { - // OK - } + static void validateServerValue(Object value, String msg) { + // OK + } } diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodBuilderSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodBuilderSpec.groovy index f2ae54c6cf..aa5a26cb5d 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodBuilderSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodBuilderSpec.groovy @@ -18,8 +18,8 @@ class JaxRsClientSpockMethodBuilderSpec extends Specification implements WireMoc response { status 200 body """{ - "property1": "a", - "property2": "b" + "property1": "a", + "property2": "b" }""" } } @@ -126,10 +126,10 @@ class JaxRsClientSpockMethodBuilderSpec extends Specification implements WireMoc status 200 body """[ { - "property1": "a" + "property1": "a" }, { - "property2": "b" + "property2": "b" }]""" } } @@ -154,10 +154,10 @@ class JaxRsClientSpockMethodBuilderSpec extends Specification implements WireMoc response { status 200 body """{ - "property1": [ - { "property2": "test1"}, - { "property3": "test2"} - ] + "property1": [ + { "property2": "test1"}, + { "property3": "test2"} + ] }""" } } @@ -183,8 +183,8 @@ class JaxRsClientSpockMethodBuilderSpec extends Specification implements WireMoc status 200 body '''\ { - "property1": "a", - "property2": {"property3": "b"} + "property1": "a", + "property2": {"property3": "b"} } ''' } diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy index a56df6014b..e3e4120e0d 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy @@ -21,8 +21,8 @@ class MockMvcSpockMethodBuilderSpec extends Specification implements WireMockStu response { status 200 body """{ - "property1": "a", - "property2": "b" + "property1": "a", + "property2": "b" }""" } } @@ -129,10 +129,10 @@ class MockMvcSpockMethodBuilderSpec extends Specification implements WireMockStu status 200 body """[ { - "property1": "a" + "property1": "a" }, { - "property2": "b" + "property2": "b" }]""" } } @@ -157,10 +157,10 @@ class MockMvcSpockMethodBuilderSpec extends Specification implements WireMockStu response { status 200 body """{ - "property1": [ - { "property2": "test1"}, - { "property3": "test2"} - ] + "property1": [ + { "property2": "test1"}, + { "property3": "test2"} + ] }""" } } @@ -186,8 +186,8 @@ class MockMvcSpockMethodBuilderSpec extends Specification implements WireMockStu status 200 body '''\ { - "property1": "a", - "property2": {"property3": "b"} + "property1": "a", + "property2": {"property3": "b"} } ''' } diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy index 2188321374..053d33217b 100755 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy @@ -85,17 +85,17 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie AssertionUtil.assertThatJsonsAreEqual(''' { "request" : { - "url" : "/ingredients", - "method" : "GET", - "headers" : { - "Content-Type" : { - "equalTo" : "application/vnd.pl.devoxx.aggregatr.v1+json" - } - } + "url" : "/ingredients", + "method" : "GET", + "headers" : { + "Content-Type" : { + "equalTo" : "application/vnd.pl.devoxx.aggregatr.v1+json" + } + } }, "response" : { - "status" : 200, - "body" : "{\\"ingredients\\":[{\\"type\\":\\"MALT\\",\\"quantity\\":100},{\\"type\\":\\"WATER\\",\\"quantity\\":200},{\\"type\\":\\"HOP\\",\\"quantity\\":300},{\\"type\\":\\"YIEST\\",\\"quantity\\":400}]}" + "status" : 200, + "body" : "{\\"ingredients\\":[{\\"type\\":\\"MALT\\",\\"quantity\\":100},{\\"type\\":\\"WATER\\",\\"quantity\\":200},{\\"type\\":\\"HOP\\",\\"quantity\\":300},{\\"type\\":\\"YIEST\\",\\"quantity\\":400}]}" } } ''', wireMockStub) @@ -132,24 +132,24 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie then: AssertionUtil.assertThatJsonsAreEqual(''' { - "request": { - "method": "POST", - "headers": { - "Content-Type": { - "equalTo": "application/x-www-form-urlencoded" - } - }, - "url": "/ws/payments", - "bodyPatterns": [ - { - "matches": "paymentType=INCOMING&transferType=BANK&amount=[0-9]{3}\\\\.[0-9]{2}&bookingDate=[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])" - } - ] - }, - "response": { - "status": 204, - "body": "{\\"paymentId\\":\\"4\\",\\"foundExistingPayment\\":false}" - } + "request": { + "method": "POST", + "headers": { + "Content-Type": { + "equalTo": "application/x-www-form-urlencoded" + } + }, + "url": "/ws/payments", + "bodyPatterns": [ + { + "matches": "paymentType=INCOMING&transferType=BANK&amount=[0-9]{3}\\\\.[0-9]{2}&bookingDate=[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])" + } + ] + }, + "response": { + "status": 204, + "body": "{\\"paymentId\\":\\"4\\",\\"foundExistingPayment\\":false}" + } } ''', wireMockStub) and: @@ -166,13 +166,13 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie response { status 200 body("""\ - { - "id": "${value(client('123'), server('321'))}", - "surname": "${value(client('Kowalsky'), server('Lewandowski'))}", - "name": "Jan", - "created" : "${$(client('2014-02-02 12:23:43'), server('2999-09-09 01:23:45'))}" - } - """ + { + "id": "${value(client('123'), server('321'))}", + "surname": "${value(client('Kowalsky'), server('Lewandowski'))}", + "name": "Jan", + "created" : "${$(client('2014-02-02 12:23:43'), server('2999-09-09 01:23:45'))}" + } + """ ) headers { header 'Content-Type': 'text/plain' @@ -185,15 +185,15 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie AssertionUtil.assertThatJsonsAreEqual((''' { "request" : { - "urlPattern" : "/[0-9]{2}", - "method" : "GET" + "urlPattern" : "/[0-9]{2}", + "method" : "GET" }, "response" : { - "status" : 200, - "body" : "{\\"created\\":\\"2014-02-02 12:23:43\\",\\"id\\":\\"123\\",\\"name\\":\\"Jan\\",\\"surname\\":\\"Kowalsky\\"}", - "headers" : { - "Content-Type" : "text/plain" - } + "status" : 200, + "body" : "{\\"created\\":\\"2014-02-02 12:23:43\\",\\"id\\":\\"123\\",\\"name\\":\\"Jan\\",\\"surname\\":\\"Kowalsky\\"}", + "headers" : { + "Content-Type" : "text/plain" + } } } '''), wireMockStub) @@ -216,10 +216,10 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie response { status 200 body("""\ - { - "name": "Jan" - } - """ + { + "name": "Jan" + } + """ ) headers { header 'Content-Type': 'text/plain' @@ -232,18 +232,18 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie AssertionUtil.assertThatJsonsAreEqual(''' { "request" : { - "urlPattern" : "/[0-9]{2}", - "method" : "GET", - "bodyPatterns" : [ { - "matchesJsonPath" : "$[?(@.name == 'Jan')]" - } ] + "urlPattern" : "/[0-9]{2}", + "method" : "GET", + "bodyPatterns" : [ { + "matchesJsonPath" : "$[?(@.name == 'Jan')]" + } ] }, "response" : { - "status" : 200, - "body" : "{\\"name\\":\\"Jan\\"}", - "headers" : { - "Content-Type" : "text/plain" - } + "status" : 200, + "body" : "{\\"name\\":\\"Jan\\"}", + "headers" : { + "Content-Type" : "text/plain" + } } } ''', wireMockStub) @@ -280,20 +280,20 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie AssertionUtil.assertThatJsonsAreEqual((''' { "request" : { - "urlPattern" : "/[0-9]{2}", - "method" : "GET", - "bodyPatterns" : [ { - "matchesJsonPath" : "$[?(@.created == '2014-02-02 12:23:43')]" - }, { - "matchesJsonPath" : "$[?(@.surname == 'Kowalsky')]" - }, { - "matchesJsonPath" : "$[?(@.name == 'Jan')]" - }, { - "matchesJsonPath" : "$[?(@.id == '123')]" - } ] + "urlPattern" : "/[0-9]{2}", + "method" : "GET", + "bodyPatterns" : [ { + "matchesJsonPath" : "$[?(@.created == '2014-02-02 12:23:43')]" + }, { + "matchesJsonPath" : "$[?(@.surname == 'Kowalsky')]" + }, { + "matchesJsonPath" : "$[?(@.name == 'Jan')]" + }, { + "matchesJsonPath" : "$[?(@.id == '123')]" + } ] }, "response" : { - "status" : 200 + "status" : 200 } } '''), wireMockStub) @@ -326,19 +326,19 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie AssertionUtil.assertThatJsonsAreEqual((''' { "request" : { - "url" : "/users", - "method" : "GET", - "bodyPatterns" : [ { - "matchesJsonPath" : "$[?(@.name == 'Jan')]" - } ], - "headers" : { - "Content-Type" : { - "equalTo" : "customtype/json" - } - } + "url" : "/users", + "method" : "GET", + "bodyPatterns" : [ { + "matchesJsonPath" : "$[?(@.name == 'Jan')]" + } ], + "headers" : { + "Content-Type" : { + "equalTo" : "customtype/json" + } + } }, "response" : { - "status" : 200 + "status" : 200 } } '''), json) @@ -548,10 +548,10 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie response { status 200 body("""\ - { - "name": "Jan" - } - """ + { + "name": "Jan" + } + """ ) headers { header 'Content-Type': 'text/plain' @@ -564,18 +564,18 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie AssertionUtil.assertThatJsonsAreEqual((''' { "request" : { - "urlPattern" : "/[0-9]{2}", - "method" : "GET", - "bodyPatterns" : [ { - "matchesJsonPath" : "$[?(@.personalId =~ /^[0-9]{11}$/)]" - } ] + "urlPattern" : "/[0-9]{2}", + "method" : "GET", + "bodyPatterns" : [ { + "matchesJsonPath" : "$[?(@.personalId =~ /^[0-9]{11}$/)]" + } ] }, "response" : { - "status" : 200, - "body" : "{\\"name\\":\\"Jan\\"}", - "headers" : { - "Content-Type" : "text/plain" - } + "status" : 200, + "body" : "{\\"name\\":\\"Jan\\"}", + "headers" : { + "Content-Type" : "text/plain" + } } } '''), wireMockStub) @@ -590,11 +590,11 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie method 'PUT' url '/fraudcheck' body(""" - { - "clientPesel":"${value(client(regex('[0-9]{10}')), server('1234567890'))}", - "loanAmount":123.123 - } - """ + { + "clientPesel":"${value(client(regex('[0-9]{10}')), server('1234567890'))}", + "loanAmount":123.123 + } + """ ) headers { header('Content-Type', 'application/vnd.fraud.v1+json') @@ -619,25 +619,25 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie AssertionUtil.assertThatJsonsAreEqual((''' { "request" : { - "url" : "/fraudcheck", - "method" : "PUT", - "bodyPatterns" : [ { - "matchesJsonPath" : "$[?(@.loanAmount == 123.123)]" - }, { - "matchesJsonPath" : "$[?(@.clientPesel =~ /[0-9]{10}/)]" - } ], - "headers" : { - "Content-Type" : { - "equalTo" : "application/vnd.fraud.v1+json" - } - } + "url" : "/fraudcheck", + "method" : "PUT", + "bodyPatterns" : [ { + "matchesJsonPath" : "$[?(@.loanAmount == 123.123)]" + }, { + "matchesJsonPath" : "$[?(@.clientPesel =~ /[0-9]{10}/)]" + } ], + "headers" : { + "Content-Type" : { + "equalTo" : "application/vnd.fraud.v1+json" + } + } }, "response" : { - "status" : 200, - "body" : "{\\"fraudCheckStatus\\":\\"OK\\",\\"rejectionReason\\":null}", - "headers" : { - "Content-Type" : "application/vnd.fraud.v1+json" - } + "status" : 200, + "body" : "{\\"fraudCheckStatus\\":\\"OK\\",\\"rejectionReason\\":null}", + "headers" : { + "Content-Type" : "application/vnd.fraud.v1+json" + } } } '''), wireMockStub) @@ -686,20 +686,20 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie "equalTo": "email" }, "sort": { - "matches": "^[0-9]{10}$" - }, - "search": { - "doesNotMatch": "^/[0-9]{2}$" - }, - "age": { - "doesNotMatch": "^\\\\w*$" - }, - "name": { - "matches": "Denis.*" - }, - "credit": { - "absent": true - } + "matches": "^[0-9]{10}$" + }, + "search": { + "doesNotMatch": "^/[0-9]{2}$" + }, + "age": { + "doesNotMatch": "^\\\\w*$" + }, + "name": { + "matches": "Denis.*" + }, + "credit": { + "absent": true + } } }, "response": { @@ -967,32 +967,32 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie AssertionUtil.assertThatJsonsAreEqual((''' { "request" : { - "urlPattern" : "/[0-9]{2}", - "method" : "GET", - "bodyPatterns" : [ { - "matchesJsonPath" : "$.errors[*][?(@.propertyName =~ /[0-9]{2}/)]" - }, { - "matchesJsonPath" : "$.errors[*][?(@.providerValue == 'Test')]" - }, { - "matchesJsonPath" : "$.errors[*][?(@.providerValue == 'Test')]" - }, { - "matchesJsonPath" : "$[?(@.lastName =~ /.*/)]" - }, { - "matchesJsonPath" : "$.errors[*][?(@.propertyName =~ /[0-9]{2}/)]" - }, { - "matchesJsonPath" : "$[?(@.birthDate =~ /[0-9]{4}-[0-9]{2}-[0-9]{2}/)]" - }, { - "matchesJsonPath" : "$[?(@.personalId =~ /[0-9]{11}/)]" - }, { - "matchesJsonPath" : "$[?(@.firstName =~ /.*/)]" - } ] + "urlPattern" : "/[0-9]{2}", + "method" : "GET", + "bodyPatterns" : [ { + "matchesJsonPath" : "$.errors[*][?(@.propertyName =~ /[0-9]{2}/)]" + }, { + "matchesJsonPath" : "$.errors[*][?(@.providerValue == 'Test')]" + }, { + "matchesJsonPath" : "$.errors[*][?(@.providerValue == 'Test')]" + }, { + "matchesJsonPath" : "$[?(@.lastName =~ /.*/)]" + }, { + "matchesJsonPath" : "$.errors[*][?(@.propertyName =~ /[0-9]{2}/)]" + }, { + "matchesJsonPath" : "$[?(@.birthDate =~ /[0-9]{4}-[0-9]{2}-[0-9]{2}/)]" + }, { + "matchesJsonPath" : "$[?(@.personalId =~ /[0-9]{11}/)]" + }, { + "matchesJsonPath" : "$[?(@.firstName =~ /.*/)]" + } ] }, "response" : { - "status" : 200, - "body" : "{\\"name\\":\\"Jan\\"}", - "headers" : { - "Content-Type" : "text/plain" - } + "status" : 200, + "body" : "{\\"name\\":\\"Jan\\"}", + "headers" : { + "Content-Type" : "text/plain" + } } } '''), wireMockStub) @@ -1031,28 +1031,28 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie AssertionUtil.assertThatJsonsAreEqual((''' { "request" : { - "url" : "/reissue-payment-order", - "method" : "POST", - "bodyPatterns" : [ { - "matchesJsonPath" : "$[?(@.loanNumber == '999997001')]" - }, { - "matchesJsonPath" : "$[?(@.username =~ /.*/)]" - }, { - "matchesJsonPath" : "$[?(@.amount =~ /[0-9.]+/)]" - }, { - "matchesJsonPath" : "$[?(@.cardId == 1)]" - }, { - "matchesJsonPath" : "$[?(@.currency == 'DKK')]" - }, { - "matchesJsonPath" : "$[?(@.applicationName =~ /.*/)]" - } ] + "url" : "/reissue-payment-order", + "method" : "POST", + "bodyPatterns" : [ { + "matchesJsonPath" : "$[?(@.loanNumber == '999997001')]" + }, { + "matchesJsonPath" : "$[?(@.username =~ /.*/)]" + }, { + "matchesJsonPath" : "$[?(@.amount =~ /[0-9.]+/)]" + }, { + "matchesJsonPath" : "$[?(@.cardId == 1)]" + }, { + "matchesJsonPath" : "$[?(@.currency == 'DKK')]" + }, { + "matchesJsonPath" : "$[?(@.applicationName =~ /.*/)]" + } ] }, "response" : { - "status" : 200, - "body" : "{\\"status\\":\\"OK\\"}", - "headers" : { - "Content-Type" : "application/json" - } + "status" : 200, + "body" : "{\\"status\\":\\"OK\\"}", + "headers" : { + "Content-Type" : "application/json" + } } } '''), json) @@ -1075,50 +1075,50 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie then: AssertionUtil.assertThatJsonsAreEqual((''' { - "request": { - "method": "POST", - "url": "test", - "bodyPatterns": [ - { - "equalTo": "" - } - ] - }, - "response": { - "status": 406 - } + "request": { + "method": "POST", + "url": "test", + "bodyPatterns": [ + { + "equalTo": "" + } + ] + }, + "response": { + "status": 406 + } } '''), json) } - def "should generate stub with priority"() { - given: - GroovyDsl groovyDsl = GroovyDsl.make { - priority 9 - request { - method('POST') - url("test") - } - response { - status 406 - } - } - when: - def json = toWireMockClientJsonStub(groovyDsl) - then: + def "should generate stub with priority"() { + given: + GroovyDsl groovyDsl = GroovyDsl.make { + priority 9 + request { + method('POST') + url("test") + } + response { + status 406 + } + } + when: + def json = toWireMockClientJsonStub(groovyDsl) + then: AssertionUtil.assertThatJsonsAreEqual((''' - { - "priority": 9, - "request": { - "method": "POST", - "url": "test" - }, - "response": { - "status": 406 - } - } - '''), json) - } + { + "priority": 9, + "request": { + "method": "POST", + "url": "test" + }, + "response": { + "status": 406 + } + } + '''), json) + } @Issue("#127") def 'should use "test" as an alias for "server"'() { @@ -1140,16 +1140,16 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie AssertionUtil.assertThatJsonsAreEqual((''' { "request" : { - "method" : "POST", - "bodyPatterns" : [ { - "matchesJsonPath" : "$[?(@.property == 'value')]" - } ] + "method" : "POST", + "bodyPatterns" : [ { + "matchesJsonPath" : "$[?(@.property == 'value')]" + } ] }, "response" : { - "status" : 200 + "status" : 200 } } - '''), wireMockStub) + '''), wireMockStub) and: stubMappingIsValidWireMockStub(wireMockStub) } diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockStubVerifier.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockStubVerifier.groovy index a970434c05..241380062f 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockStubVerifier.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockStubVerifier.groovy @@ -5,12 +5,12 @@ import java.util.regex.Pattern trait WireMockStubVerifier { - void stubMappingIsValidWireMockStub(String mappingDefinition) { - StubMapping stubMapping = StubMapping.buildFrom(mappingDefinition) - stubMapping.request.bodyPatterns.findAll { it.matches }.every { - Pattern.compile(it.matches) - } - assert !mappingDefinition.contains('DslProperty') - } + void stubMappingIsValidWireMockStub(String mappingDefinition) { + StubMapping stubMapping = StubMapping.buildFrom(mappingDefinition) + stubMapping.request.bodyPatterns.findAll { it.matches }.every { + Pattern.compile(it.matches) + } + assert !mappingDefinition.contains('DslProperty') + } } diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/internal/ExecutionPropertySpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/internal/ExecutionPropertySpec.groovy index d8660a8433..35e0fbe83a 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/internal/ExecutionPropertySpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/internal/ExecutionPropertySpec.groovy @@ -4,16 +4,16 @@ import spock.lang.Specification class ExecutionPropertySpec extends Specification { - def 'should insert passed value in place of $it placeholder'() { - given: - String commandToExecute = 'commandToExecute($it)' - ExecutionProperty executionProperty = new ExecutionProperty(commandToExecute) - and: - String valueToInsert = 'someObject.itsValue' - when: - String commandWithInsertedValue = executionProperty.insertValue(valueToInsert) - then: - 'commandToExecute(someObject.itsValue)' == commandWithInsertedValue - } + def 'should insert passed value in place of $it placeholder'() { + given: + String commandToExecute = 'commandToExecute($it)' + ExecutionProperty executionProperty = new ExecutionProperty(commandToExecute) + and: + String valueToInsert = 'someObject.itsValue' + when: + String commandWithInsertedValue = executionProperty.insertValue(valueToInsert) + then: + 'commandToExecute(someObject.itsValue)' == commandWithInsertedValue + } } diff --git a/accurest-core/src/test/resources/dsl/basic/sampleDsl.groovy b/accurest-core/src/test/resources/dsl/basic/sampleDsl.groovy index 95d19e6b87..4405a9920e 100644 --- a/accurest-core/src/test/resources/dsl/basic/sampleDsl.groovy +++ b/accurest-core/src/test/resources/dsl/basic/sampleDsl.groovy @@ -5,23 +5,23 @@ io.codearte.accurest.dsl.GroovyDsl.make { header 'Content-Type': 'application/json' } body("""\ - { - "name": "Jan", - "id": "${value(client('abc'), server('def'))}", - } - """ + { + "name": "Jan", + "id": "${value(client('abc'), server('def'))}", + } + """ ) url $(client('/[0-9]{2}'), server('/12')) } response { status 200 body("""\ - { - "name": "Jan", - "id": "${value(client('123'), server('321'))}", + { + "name": "Jan", + "id": "${value(client('123'), server('321'))}", "surname": "${value(client('Kowalsky'), server('$checkIfSurnameValid($value)'))}" - } - """ + } + """ ) headers { header 'Content-Type': 'text/plain' diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/build.gradle b/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/build.gradle index 48929c705b..52e2415869 100644 --- a/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/build.gradle +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/build.gradle @@ -1,71 +1,71 @@ buildscript { - repositories { - mavenCentral() - } + repositories { + mavenCentral() + } } apply plugin: 'groovy' apply plugin: 'accurest' ext { - contractsDir = file("${project.rootDir}/repository/mappings/com/ofg/twitter-places-analyzer") - wireMockStubsOutputDirRoot = file("${project.buildDir}/production/${project.name}-stubs/") - wireMockStubsOutputDir = new File(wireMockStubsOutputDirRoot, 'repository/mappings/') + contractsDir = file("${project.rootDir}/repository/mappings/com/ofg/twitter-places-analyzer") + wireMockStubsOutputDirRoot = file("${project.buildDir}/production/${project.name}-stubs/") + wireMockStubsOutputDir = new File(wireMockStubsOutputDirRoot, 'repository/mappings/') } configurations { - all { - resolutionStrategy { - eachDependency { DependencyResolveDetails details -> - // To prevent an accidental usage of groovy-all.jar and groovy.jar in different versions - // all modularized Groovy jars are replaced with groovy-all.jar by default. - if (details.requested.group == 'org.codehaus.groovy' && details.requested.name != "groovy-all") { - details.useTarget("org.codehaus.groovy:groovy-all:${details.requested.version}") - } - } - } - } + all { + resolutionStrategy { + eachDependency { DependencyResolveDetails details -> + // To prevent an accidental usage of groovy-all.jar and groovy.jar in different versions + // all modularized Groovy jars are replaced with groovy-all.jar by default. + if (details.requested.group == 'org.codehaus.groovy' && details.requested.name != "groovy-all") { + details.useTarget("org.codehaus.groovy:groovy-all:${details.requested.version}") + } + } + } + } } repositories { - mavenCentral() + mavenCentral() } dependencies { - compile "org.springframework:spring-web:$springVersion" - compile "org.springframework:spring-context-support:$springVersion" - compile "org.codehaus.groovy:groovy-all:2.4.1" - compile 'com.fasterxml.jackson.core:jackson-databind:2.4.4' - compile "org.codehaus.jackson:jackson-mapper-asl:$jacksonMapper" - compile "org.codehaus.jackson:jackson-core-asl:$jacksonMapper" - compile 'com.jayway.jsonpath:json-path-assert:1.2.0' + compile "org.springframework:spring-web:$springVersion" + compile "org.springframework:spring-context-support:$springVersion" + compile "org.codehaus.groovy:groovy-all:2.4.1" + compile 'com.fasterxml.jackson.core:jackson-databind:2.4.4' + compile "org.codehaus.jackson:jackson-mapper-asl:$jacksonMapper" + compile "org.codehaus.jackson:jackson-core-asl:$jacksonMapper" + compile 'com.jayway.jsonpath:json-path-assert:1.2.0' - testCompile('com.github.tomakehurst:wiremock:1.53') { - exclude group: 'org.mortbay.jetty', module: 'servlet-api' - } - testCompile "org.spockframework:spock-spring:0.7-groovy-2.0" - testCompile "com.jayway.restassured:rest-assured:$restAssuredVersion" - testCompile "com.jayway.restassured:spring-mock-mvc:$restAssuredVersion" - testCompile "javax.servlet:javax.servlet-api:3.0.1" //provided - testCompile "ch.qos.logback:logback-classic:1.1.2" + testCompile('com.github.tomakehurst:wiremock:1.53') { + exclude group: 'org.mortbay.jetty', module: 'servlet-api' + } + testCompile "org.spockframework:spock-spring:0.7-groovy-2.0" + testCompile "com.jayway.restassured:rest-assured:$restAssuredVersion" + testCompile "com.jayway.restassured:spring-mock-mvc:$restAssuredVersion" + testCompile "javax.servlet:javax.servlet-api:3.0.1" //provided + testCompile "ch.qos.logback:logback-classic:1.1.2" } accurest { - baseClassForTests = 'com.ofg.twitter.places.BaseMockMvcSpec' - basePackageForTests = 'accurest' - contractsDslDir = contractsDir -// generatedTestSourcesDir = file("${project.rootDir}/src/test/groovy/") - stubsOutputDir = wireMockStubsOutputDir + baseClassForTests = 'com.ofg.twitter.places.BaseMockMvcSpec' + basePackageForTests = 'accurest' + contractsDslDir = contractsDir +// generatedTestSourcesDir = file("${project.rootDir}/src/test/groovy/") + stubsOutputDir = wireMockStubsOutputDir } //TODO: Put it into the plugin task createWireMockStubsOutputDir << { - wireMockStubsOutputDir.mkdirs() + wireMockStubsOutputDir.mkdirs() } generateWireMockClientStubs.dependsOn { createWireMockStubsOutputDir } generateAccurest.dependsOn generateWireMockClientStubs wrapper { - gradleVersion '2.2.1' + gradleVersion '2.2.1' } diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/repository/mappings/com/ofg/twitter-places-analyzer/pairId/colleratePlacesFromTweet.groovy b/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/repository/mappings/com/ofg/twitter-places-analyzer/pairId/colleratePlacesFromTweet.groovy index 6fe36c6a89..826f71b791 100644 --- a/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/repository/mappings/com/ofg/twitter-places-analyzer/pairId/colleratePlacesFromTweet.groovy +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/repository/mappings/com/ofg/twitter-places-analyzer/pairId/colleratePlacesFromTweet.groovy @@ -1,18 +1,18 @@ io.codearte.accurest.dsl.GroovyDsl.make { priority 2 - request { - method 'PUT' - url '/api/12' - headers { - header 'Content-Type': 'application/json' - } - body '''\ - [{ - "text": "Gonna see you at Warsaw" - }] + request { + method 'PUT' + url '/api/12' + headers { + header 'Content-Type': 'application/json' + } + body '''\ + [{ + "text": "Gonna see you at Warsaw" + }] ''' - } - response { - status 200 - } + } + response { + status 200 + } } \ No newline at end of file diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/repository/mappings/com/ofg/twitter-places-analyzer/pairId/moreComplexVersion.groovy b/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/repository/mappings/com/ofg/twitter-places-analyzer/pairId/moreComplexVersion.groovy index fdeb658215..943fbc23b9 100644 --- a/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/repository/mappings/com/ofg/twitter-places-analyzer/pairId/moreComplexVersion.groovy +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/repository/mappings/com/ofg/twitter-places-analyzer/pairId/moreComplexVersion.groovy @@ -1,21 +1,21 @@ io.codearte.accurest.dsl.GroovyDsl.make { - request { - method 'PUT' - url $(client(regex('^/api/[0-9]{2}$')), server('/api/12')) - headers { - header 'Content-Type': 'application/json' - } - body '''\ - [{ - "text": "Gonna see you at Warsaw" - }] + request { + method 'PUT' + url $(client(regex('^/api/[0-9]{2}$')), server('/api/12')) + headers { + header 'Content-Type': 'application/json' + } + body '''\ + [{ + "text": "Gonna see you at Warsaw" + }] ''' - } - response { - body ( - path: $(client('/api/12'), server(regex('^/api/[0-9]{2}$'))), - correlationId: $(client('1223456'), server(execute('isProperCorrelationId($it)'))) - ) - status 200 - } + } + response { + body ( + path: $(client('/api/12'), server(regex('^/api/[0-9]{2}$'))), + correlationId: $(client('1223456'), server(execute('isProperCorrelationId($it)'))) + ) + status 200 + } } \ No newline at end of file diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/src/main/groovy/com/ofg/twitter/place/PairIdController.groovy b/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/src/main/groovy/com/ofg/twitter/place/PairIdController.groovy index 4f49f1ed3b..3a7671793c 100644 --- a/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/src/main/groovy/com/ofg/twitter/place/PairIdController.groovy +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/src/main/groovy/com/ofg/twitter/place/PairIdController.groovy @@ -16,21 +16,21 @@ import static org.springframework.web.bind.annotation.RequestMethod.PUT @TypeChecked class PairIdController { - @RequestMapping( - value = '{pairId}', - method = PUT, - consumes = MediaType.APPLICATION_JSON_VALUE, - produces = MediaType.APPLICATION_JSON_VALUE) - String getPlacesFromTweets(@PathVariable long pairId, @RequestBody List tweets) { - log.info("Inside PairIdController, doing very important logic") - if (tweets?.text != ["Gonna see you at Warsaw"]) { - throw new IllegalArgumentException("Wrong text in tweet: ${tweets?.text}") - } - return """ - { - "path" : "/api/$pairId", - "correlationId" : 123456 - } - """ - } + @RequestMapping( + value = '{pairId}', + method = PUT, + consumes = MediaType.APPLICATION_JSON_VALUE, + produces = MediaType.APPLICATION_JSON_VALUE) + String getPlacesFromTweets(@PathVariable long pairId, @RequestBody List tweets) { + log.info("Inside PairIdController, doing very important logic") + if (tweets?.text != ["Gonna see you at Warsaw"]) { + throw new IllegalArgumentException("Wrong text in tweet: ${tweets?.text}") + } + return """ + { + "path" : "/api/$pairId", + "correlationId" : 123456 + } + """ + } } diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/src/main/groovy/com/ofg/twitter/place/Tweet.java b/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/src/main/groovy/com/ofg/twitter/place/Tweet.java index 96fdd52262..5d1e9c2835 100644 --- a/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/src/main/groovy/com/ofg/twitter/place/Tweet.java +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/src/main/groovy/com/ofg/twitter/place/Tweet.java @@ -1,13 +1,13 @@ package com.ofg.twitter.place; public class Tweet { - private String text; + private String text; - public String getText() { - return text; - } + public String getText() { + return text; + } - public void setText(String text) { - this.text = text; - } + public void setText(String text) { + this.text = text; + } } diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/src/test/groovy/com/ofg/twitter/places/AcceptanceSpec.groovy b/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/src/test/groovy/com/ofg/twitter/places/AcceptanceSpec.groovy index 6a3bef53d7..458e275daf 100644 --- a/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/src/test/groovy/com/ofg/twitter/places/AcceptanceSpec.groovy +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/src/test/groovy/com/ofg/twitter/places/AcceptanceSpec.groovy @@ -11,13 +11,13 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. class AcceptanceSpec extends Specification { - def "should have controller up and running"() { - given: - MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new PairIdController()).build() - expect: - mockMvc.perform(put("/api/${1}"). - contentType(MediaType.APPLICATION_JSON). - content("""[{"text":"Gonna see you at Warsaw"}]""")). - andExpect(status().isOk()) - } + def "should have controller up and running"() { + given: + MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new PairIdController()).build() + expect: + mockMvc.perform(put("/api/${1}"). + contentType(MediaType.APPLICATION_JSON). + content("""[{"text":"Gonna see you at Warsaw"}]""")). + andExpect(status().isOk()) + } } diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/src/test/groovy/com/ofg/twitter/places/BaseMockMvcSpec.groovy b/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/src/test/groovy/com/ofg/twitter/places/BaseMockMvcSpec.groovy index cade991eaa..6b8e7e630e 100644 --- a/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/src/test/groovy/com/ofg/twitter/places/BaseMockMvcSpec.groovy +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/src/test/groovy/com/ofg/twitter/places/BaseMockMvcSpec.groovy @@ -6,11 +6,11 @@ import spock.lang.Specification abstract class BaseMockMvcSpec extends Specification { - def setup() { - RestAssuredMockMvc.standaloneSetup(new PairIdController()) - } + def setup() { + RestAssuredMockMvc.standaloneSetup(new PairIdController()) + } - void isProperCorrelationId(Integer correlationId) { - assert correlationId == 123456 - } + void isProperCorrelationId(Integer correlationId) { + assert correlationId == 123456 + } } diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsFraud.groovy b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsFraud.groovy index a47dff32e4..44b1c08604 100644 --- a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsFraud.groovy +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsFraud.groovy @@ -1,27 +1,27 @@ io.codearte.accurest.dsl.GroovyDsl.make { - request { - method """PUT""" - url """/fraudcheck""" - body(""" - { - "clientPesel":"${value(client(regex('[0-9]{10}')), server('1234567890'))}", - "loanAmount":99999} - """ - ) - headers { - header("""Content-Type""", """application/vnd.fraud.v1+json""") - } - - } - response { - status 200 - body( """{ - "fraudCheckStatus": "${value(client('FRAUD'), server(regex('[A-Z]{5}')))}", - "rejectionReason": "Amount too high" + request { + method """PUT""" + url """/fraudcheck""" + body(""" + { + "clientPesel":"${value(client(regex('[0-9]{10}')), server('1234567890'))}", + "loanAmount":99999} + """ + ) + headers { + header("""Content-Type""", """application/vnd.fraud.v1+json""") + } + + } + response { + status 200 + body( """{ + "fraudCheckStatus": "${value(client('FRAUD'), server(regex('[A-Z]{5}')))}", + "rejectionReason": "Amount too high" }""") - headers { - header('Content-Type': 'application/vnd.fraud.v1+json') - } - } - + headers { + header('Content-Type': 'application/vnd.fraud.v1+json') + } + } + } diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.groovy b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.groovy index fa8cd88ade..7bc64d0dac 100644 --- a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.groovy +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.groovy @@ -1,28 +1,28 @@ io.codearte.accurest.dsl.GroovyDsl.make { - request { - method 'PUT' - url '/fraudcheck' - body(""" - { - "clientPesel":"${value(client(regex('[0-9]{10}')), server('1234567890'))}", - "loanAmount":123.123 - } - """ - ) - headers { - header('Content-Type', 'application/vnd.fraud.v1+json') - } - - } - response { - status 200 - body( - fraudCheckStatus: "OK", - rejectionReason: $(client(null), server(execute('assertThatRejectionReasonIsNull($it)'))) - ) - headers { - header('Content-Type': 'application/vnd.fraud.v1+json') - } - } - + request { + method 'PUT' + url '/fraudcheck' + body(""" + { + "clientPesel":"${value(client(regex('[0-9]{10}')), server('1234567890'))}", + "loanAmount":123.123 + } + """ + ) + headers { + header('Content-Type', 'application/vnd.fraud.v1+json') + } + + } + response { + status 200 + body( + fraudCheckStatus: "OK", + rejectionReason: $(client(null), server(execute('assertThatRejectionReasonIsNull($it)'))) + ) + headers { + header('Content-Type': 'application/vnd.fraud.v1+json') + } + } + } diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/FraudRestApplication.java b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/FraudRestApplication.java index ce70aa1050..83e15edc59 100644 --- a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/FraudRestApplication.java +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/src/main/java/com/blogspot/toomuchcoding/frauddetection/FraudRestApplication.java @@ -5,9 +5,9 @@ import java.util.Set; public class FraudRestApplication extends javax.ws.rs.core.Application { - @Override - public Set> getClasses() { - return Collections.>singleton(FraudDetectionController.class); - } + @Override + public Set> getClasses() { + return Collections.>singleton(FraudDetectionController.class); + } } \ No newline at end of file diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsFraud.json b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsFraud.json index 90383f2ce1..7229872299 100644 --- a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsFraud.json +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsFraud.json @@ -1,23 +1,23 @@ { - "request": { - "method": "PUT", - "headers": { - "Content-Type": { - "equalTo": "application/vnd.fraud.v1+json" - } - }, - "url": "/fraudcheck", - "bodyPatterns": [ - { - "matches": "\\s*\\{\\s*\"clientPesel\"\\s*:\\s*\"?[0-9]{10}\"?\\s*,\\s*\"loanAmount\"\\s*:\\s*\"?99999\"?\\s*\\}\\s*" - } - ] - }, - "response": { - "status": 200, - "headers": { - "Content-Type": "application/vnd.fraud.v1+json" - }, - "body": "{\"fraudCheckStatus\":\"FRAUD\",\"rejectionReason\":\"Amount too high\"}" - } + "request": { + "method": "PUT", + "headers": { + "Content-Type": { + "equalTo": "application/vnd.fraud.v1+json" + } + }, + "url": "/fraudcheck", + "bodyPatterns": [ + { + "matches": "\\s*\\{\\s*\"clientPesel\"\\s*:\\s*\"?[0-9]{10}\"?\\s*,\\s*\"loanAmount\"\\s*:\\s*\"?99999\"?\\s*\\}\\s*" + } + ] + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/vnd.fraud.v1+json" + }, + "body": "{\"fraudCheckStatus\":\"FRAUD\",\"rejectionReason\":\"Amount too high\"}" + } } \ No newline at end of file diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.json b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.json index 2f47855910..5a251171c7 100644 --- a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.json +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.json @@ -1,23 +1,23 @@ { - "request": { - "method": "PUT", - "headers": { - "Content-Type": { - "equalTo": "application/vnd.fraud.v1+json" - } - }, - "url": "/fraudcheck", - "bodyPatterns": [ - { - "matches": "\\s*\\{\\s*\"clientPesel\"\\s*:\\s*\"?[0-9]{10}\"?\\s*,\\s*\"loanAmount\"\\s*:\\s*\"?123.123\"?\\s*\\}\\s*" - } - ] - }, - "response": { - "status": 200, - "headers": { - "Content-Type": "application/vnd.fraud.v1+json" - }, - "body": "{\"fraudCheckStatus\":\"OK\",\"rejectionReason\":null}" - } + "request": { + "method": "PUT", + "headers": { + "Content-Type": { + "equalTo": "application/vnd.fraud.v1+json" + } + }, + "url": "/fraudcheck", + "bodyPatterns": [ + { + "matches": "\\s*\\{\\s*\"clientPesel\"\\s*:\\s*\"?[0-9]{10}\"?\\s*,\\s*\"loanAmount\"\\s*:\\s*\"?123.123\"?\\s*\\}\\s*" + } + ] + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/vnd.fraud.v1+json" + }, + "body": "{\"fraudCheckStatus\":\"OK\",\"rejectionReason\":null}" + } } \ No newline at end of file diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsFraud.groovy b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsFraud.groovy index a47dff32e4..44b1c08604 100644 --- a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsFraud.groovy +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsFraud.groovy @@ -1,27 +1,27 @@ io.codearte.accurest.dsl.GroovyDsl.make { - request { - method """PUT""" - url """/fraudcheck""" - body(""" - { - "clientPesel":"${value(client(regex('[0-9]{10}')), server('1234567890'))}", - "loanAmount":99999} - """ - ) - headers { - header("""Content-Type""", """application/vnd.fraud.v1+json""") - } - - } - response { - status 200 - body( """{ - "fraudCheckStatus": "${value(client('FRAUD'), server(regex('[A-Z]{5}')))}", - "rejectionReason": "Amount too high" + request { + method """PUT""" + url """/fraudcheck""" + body(""" + { + "clientPesel":"${value(client(regex('[0-9]{10}')), server('1234567890'))}", + "loanAmount":99999} + """ + ) + headers { + header("""Content-Type""", """application/vnd.fraud.v1+json""") + } + + } + response { + status 200 + body( """{ + "fraudCheckStatus": "${value(client('FRAUD'), server(regex('[A-Z]{5}')))}", + "rejectionReason": "Amount too high" }""") - headers { - header('Content-Type': 'application/vnd.fraud.v1+json') - } - } - + headers { + header('Content-Type': 'application/vnd.fraud.v1+json') + } + } + } diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.groovy b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.groovy index fa8cd88ade..7bc64d0dac 100644 --- a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.groovy +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.groovy @@ -1,28 +1,28 @@ io.codearte.accurest.dsl.GroovyDsl.make { - request { - method 'PUT' - url '/fraudcheck' - body(""" - { - "clientPesel":"${value(client(regex('[0-9]{10}')), server('1234567890'))}", - "loanAmount":123.123 - } - """ - ) - headers { - header('Content-Type', 'application/vnd.fraud.v1+json') - } - - } - response { - status 200 - body( - fraudCheckStatus: "OK", - rejectionReason: $(client(null), server(execute('assertThatRejectionReasonIsNull($it)'))) - ) - headers { - header('Content-Type': 'application/vnd.fraud.v1+json') - } - } - + request { + method 'PUT' + url '/fraudcheck' + body(""" + { + "clientPesel":"${value(client(regex('[0-9]{10}')), server('1234567890'))}", + "loanAmount":123.123 + } + """ + ) + headers { + header('Content-Type', 'application/vnd.fraud.v1+json') + } + + } + response { + status 200 + body( + fraudCheckStatus: "OK", + rejectionReason: $(client(null), server(execute('assertThatRejectionReasonIsNull($it)'))) + ) + headers { + header('Content-Type': 'application/vnd.fraud.v1+json') + } + } + } diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsFraud.json b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsFraud.json index 610b4ae1b1..157726ca2e 100644 --- a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsFraud.json +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsFraud.json @@ -1,23 +1,23 @@ { - "request": { - "method": "PUT", - "headers": { - "Content-Type": { - "equalTo": "application/vnd.fraud.v1+json" - } - }, - "url": "/fraudcheck", - "bodyPatterns": [ - { - "matches": "{\"clientPesel\":\"[0-9]{10}\",\"loanAmount\":\"99999\"}" - } - ] - }, - "response": { - "status": 200, - "headers": { - "Content-Type": "application/vnd.fraud.v1+json" - }, - "body": "{\"fraudCheckStatus\":\"FRAUD\",\"rejectionReason\":\"Amount too high\"}" - } + "request": { + "method": "PUT", + "headers": { + "Content-Type": { + "equalTo": "application/vnd.fraud.v1+json" + } + }, + "url": "/fraudcheck", + "bodyPatterns": [ + { + "matches": "{\"clientPesel\":\"[0-9]{10}\",\"loanAmount\":\"99999\"}" + } + ] + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/vnd.fraud.v1+json" + }, + "body": "{\"fraudCheckStatus\":\"FRAUD\",\"rejectionReason\":\"Amount too high\"}" + } } \ No newline at end of file diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.json b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.json index af5792092c..afa27159d9 100644 --- a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.json +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/loanApplicationService/src/test/resources/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.json @@ -1,23 +1,23 @@ { - "request": { - "method": "PUT", - "headers": { - "Content-Type": { - "equalTo": "application/vnd.fraud.v1+json" - } - }, - "url": "/fraudcheck", - "bodyPatterns": [ - { - "matches": "{\"clientPesel\":\"[0-9]{10}\",\"loanAmount\":\"123.123\"}" - } - ] - }, - "response": { - "status": 200, - "headers": { - "Content-Type": "application/vnd.fraud.v1+json" - }, - "body": "{\"fraudCheckStatus\":\"OK\",\"rejectionReason\":null}" - } + "request": { + "method": "PUT", + "headers": { + "Content-Type": { + "equalTo": "application/vnd.fraud.v1+json" + } + }, + "url": "/fraudcheck", + "bodyPatterns": [ + { + "matches": "{\"clientPesel\":\"[0-9]{10}\",\"loanAmount\":\"123.123\"}" + } + ] + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/vnd.fraud.v1+json" + }, + "body": "{\"fraudCheckStatus\":\"OK\",\"rejectionReason\":null}" + } } \ No newline at end of file diff --git a/build.gradle b/build.gradle index c1739aa58c..7f78c6ad8a 100644 --- a/build.gradle +++ b/build.gradle @@ -24,8 +24,7 @@ scmVersion { } allprojects { - //project.version = scmVersion.version - project.version = '0.9.1-SNAPSHOT' + project.version = scmVersion.version } apply plugin: 'io.codearte.nexus-staging' From 9d0976e23de9635811669d1b9f16f6dda94971e1 Mon Sep 17 00:00:00 2001 From: Jakub Kubrynski Date: Thu, 3 Sep 2015 17:38:09 +0200 Subject: [PATCH 088/119] Release version: 0.9.1 [ci skip] From b4689efd363a531635afd1b12254f8671ba622c8 Mon Sep 17 00:00:00 2001 From: Mariusz Smykula Date: Wed, 9 Sep 2015 20:01:26 +0200 Subject: [PATCH 089/119] Migration to wiremock 2.0 --- .../dsl/WireMockResponseStubStrategy.groovy | 38 ++++++++++--------- build.gradle | 4 +- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockResponseStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockResponseStubStrategy.groovy index 5af5898257..3f63e6433c 100755 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockResponseStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockResponseStubStrategy.groovy @@ -1,4 +1,6 @@ package io.codearte.accurest.dsl + +import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder import com.github.tomakehurst.wiremock.http.HttpHeader import com.github.tomakehurst.wiremock.http.HttpHeaders import com.github.tomakehurst.wiremock.http.ResponseDefinition @@ -25,30 +27,30 @@ class WireMockResponseStubStrategy extends BaseWireMockStubStrategy { @PackageScope ResponseDefinition buildClientResponseContent() { - ResponseDefinition responseDefinition = new ResponseDefinition() - responseDefinition.setStatus(response.status.clientValue as Integer) - appendHeaders(responseDefinition) - appendBody(responseDefinition) - return responseDefinition + ResponseDefinitionBuilder builder = new ResponseDefinitionBuilder() + .withStatus(response.status.clientValue as Integer) + appendHeaders(builder) + appendBody(builder) + return builder.build() } - private void appendHeaders(ResponseDefinition responseDefinition) { - if(!(response.headers)) { - return + private void appendHeaders(ResponseDefinitionBuilder builder) { + if (response.headers) { + builder.withHeaders(new HttpHeaders(response.headers.entries?.collect { + new HttpHeader(it.name, it.clientValue.toString()) + })) } - responseDefinition.setHeaders(new HttpHeaders(response.headers.entries?.collect { new HttpHeader(it.name, it.clientValue.toString()) })) } - private void appendBody(ResponseDefinition responseDefinition) { - if (!response.body) { - return + private void appendBody(ResponseDefinitionBuilder builder) { + if (response.body) { + Object body = response.body.clientValue + ContentType contentType = recognizeContentTypeFromHeader(response.headers) + if (contentType == ContentType.UNKNOWN) { + contentType = recognizeContentTypeFromContent(body) + } + builder.withBody(parseBody(body, contentType)) } - Object body = response.body.clientValue - ContentType contentType = recognizeContentTypeFromHeader(response.headers) - if (contentType == ContentType.UNKNOWN) { - contentType = recognizeContentTypeFromContent(body) - } - responseDefinition.setBody(parseBody(body, contentType)) } diff --git a/build.gradle b/build.gradle index 7f78c6ad8a..efabdd6b36 100644 --- a/build.gradle +++ b/build.gradle @@ -71,7 +71,7 @@ project(':accurest-core') { compile 'com.google.code.gson:gson:2.3.1' compile 'com.fasterxml.jackson.core:jackson-databind:2.4.5' compile 'asm:asm:3.3.1' - compile 'com.blogspot.toomuchcoding:wiremock:0.0.1' + compile 'com.github.tomakehurst:wiremock:2.0.2-beta' testCompile 'cglib:cglib-nodep:2.2' testCompile 'org.objenesis:objenesis:2.1' testCompile project(':accurest-testing-utils') @@ -93,7 +93,7 @@ project(':accurest-converters') { compile 'org.apache.commons:commons-lang3:[3.0,)' compile 'commons-io:commons-io:[2.0,)' compile 'dk.brics.automaton:automaton:1.11-8' // needed for Xeger - testCompile 'com.blogspot.toomuchcoding:wiremock:0.0.1' + testCompile 'com.github.tomakehurst:wiremock:2.0.2-beta' testCompile 'org.hamcrest:hamcrest-all:1.3' } } From 06bf6a39099210f79b98aa92b10b187a3c5a4665 Mon Sep 17 00:00:00 2001 From: Mariusz Smykula Date: Thu, 10 Sep 2015 12:31:23 +0200 Subject: [PATCH 090/119] gradle plugin update to wiremock 2 --- .../accurest/plugin/AccurestGradlePlugin.groovy | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/AccurestGradlePlugin.groovy b/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/AccurestGradlePlugin.groovy index e7c9feafde..8a6b55ac23 100644 --- a/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/AccurestGradlePlugin.groovy +++ b/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/AccurestGradlePlugin.groovy @@ -30,20 +30,7 @@ class AccurestGradlePlugin implements Plugin { createGenerateTestsTask(extension) createAndConfigureGenerateWireMockClientStubsFromDslTask(extension) deprecatedCreateAndConfigureGenerateWiremockClientStubsFromDslTask() - project.dependencies.add("testCompile", "com.blogspot.toomuchcoding:wiremock:0.0.1") - project.repositories { jcenter() } - project.configurations { - all { - resolutionStrategy { - eachDependency { DependencyResolveDetails details -> - if (details.requested.group == 'com.github.tomakehurst' && details.requested.name == "wiremock") { - details.useTarget("com.blogspot.toomuchcoding:wiremock:0.0.1") - } - } - } - } - } - + project.dependencies.add("testCompile", "com.github.tomakehurst:wiremock:2.0.2-beta") project.afterEvaluate { def hasIdea = project.plugins.findPlugin(IDEA_PLUGIN_CLASS) From fe1aa54be5d400f2ef324b9f8e3f1766289f46bf Mon Sep 17 00:00:00 2001 From: Jakub Kubrynski Date: Thu, 17 Sep 2015 13:38:01 +0200 Subject: [PATCH 091/119] Fixes #146 - NPE on queryParametrs --- .../builder/MockMvcSpockMethodBodyBuilder.groovy | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBodyBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBodyBuilder.groovy index fed69dc27b..af84103be2 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBodyBuilder.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBodyBuilder.groovy @@ -1,4 +1,5 @@ package io.codearte.accurest.builder + import groovy.transform.PackageScope import groovy.transform.TypeChecked import groovy.transform.TypeCheckingMode @@ -74,12 +75,15 @@ class MockMvcSpockMethodBodyBuilder extends SpockMethodBodyBuilder { @TypeChecked(TypeCheckingMode.SKIP) protected String buildUrlFromUrlPath(UrlPath urlPath) { - String params = urlPath.queryParameters.parameters - .findAll(this.&allowedQueryParameter) - .inject([] as List) { List result, QueryParameter param -> - result << "${param.name}=${resolveParamValue(param).toString()}" - } - .join('&') + String params = "" + if (urlPath.queryParameters) { + params = urlPath.queryParameters.parameters + .findAll(this.&allowedQueryParameter) + .inject([] as List) { List result, QueryParameter param -> + result << "${param.name}=${resolveParamValue(param).toString()}" + } + .join('&') + } return "$urlPath.serverValue?$params" } From 1ba1e4f5ea8b4ae06e12b79f3c97f6b3ba63809e Mon Sep 17 00:00:00 2001 From: Jakub Kubrynski Date: Thu, 17 Sep 2015 13:38:44 +0200 Subject: [PATCH 092/119] Release version: 0.9.2 [ci skip] From f3d097fb276e11b458418f09f463e6289a6683d8 Mon Sep 17 00:00:00 2001 From: Mariusz Smykula Date: Mon, 28 Sep 2015 20:04:51 +0200 Subject: [PATCH 093/119] wiremock update --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index efabdd6b36..fda2787073 100644 --- a/build.gradle +++ b/build.gradle @@ -71,7 +71,7 @@ project(':accurest-core') { compile 'com.google.code.gson:gson:2.3.1' compile 'com.fasterxml.jackson.core:jackson-databind:2.4.5' compile 'asm:asm:3.3.1' - compile 'com.github.tomakehurst:wiremock:2.0.2-beta' + compile 'com.github.tomakehurst:wiremock:2.0.4-beta' testCompile 'cglib:cglib-nodep:2.2' testCompile 'org.objenesis:objenesis:2.1' testCompile project(':accurest-testing-utils') From ac3b87bd06d25d4959f67712d53197c72199aed1 Mon Sep 17 00:00:00 2001 From: Mariusz Smykula Date: Wed, 30 Sep 2015 10:36:33 +0200 Subject: [PATCH 094/119] configuration update --- .../accurest/plugin/AccurestGradlePlugin.groovy | 2 +- .../functionalTest/bootSimple/build.gradle | 12 +++++------- .../functionalTest/bootSimple/gradle.properties | 2 +- .../sampleJerseyProject/build.gradle | 15 ++++++--------- .../functionalTest/sampleProject/build.gradle | 8 +++----- build.gradle | 9 ++++----- gradle.properties | 4 +++- 7 files changed, 23 insertions(+), 29 deletions(-) diff --git a/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/AccurestGradlePlugin.groovy b/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/AccurestGradlePlugin.groovy index 8a6b55ac23..efd3f76e92 100644 --- a/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/AccurestGradlePlugin.groovy +++ b/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/AccurestGradlePlugin.groovy @@ -30,7 +30,7 @@ class AccurestGradlePlugin implements Plugin { createGenerateTestsTask(extension) createAndConfigureGenerateWireMockClientStubsFromDslTask(extension) deprecatedCreateAndConfigureGenerateWiremockClientStubsFromDslTask() - project.dependencies.add("testCompile", "com.github.tomakehurst:wiremock:2.0.2-beta") + project.dependencies.add("testCompile", "com.github.tomakehurst:wiremock:2.0.4-beta") project.afterEvaluate { def hasIdea = project.plugins.findPlugin(IDEA_PLUGIN_CLASS) diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/build.gradle b/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/build.gradle index 52e2415869..e392e694c3 100644 --- a/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/build.gradle +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/build.gradle @@ -34,19 +34,17 @@ repositories { dependencies { compile "org.springframework:spring-web:$springVersion" compile "org.springframework:spring-context-support:$springVersion" - compile "org.codehaus.groovy:groovy-all:2.4.1" + compile "org.codehaus.groovy:groovy-all:2.4.4" compile 'com.fasterxml.jackson.core:jackson-databind:2.4.4' compile "org.codehaus.jackson:jackson-mapper-asl:$jacksonMapper" compile "org.codehaus.jackson:jackson-core-asl:$jacksonMapper" - compile 'com.jayway.jsonpath:json-path-assert:1.2.0' + compile 'com.jayway.jsonpath:json-path-assert:2.0.0' - testCompile('com.github.tomakehurst:wiremock:1.53') { - exclude group: 'org.mortbay.jetty', module: 'servlet-api' - } - testCompile "org.spockframework:spock-spring:0.7-groovy-2.0" + testCompile "com.github.tomakehurst:wiremock:2.0.4-beta" + testCompile "org.spockframework:spock-spring:1.0-groovy-2.4" testCompile "com.jayway.restassured:rest-assured:$restAssuredVersion" testCompile "com.jayway.restassured:spring-mock-mvc:$restAssuredVersion" - testCompile "javax.servlet:javax.servlet-api:3.0.1" //provided + testCompile 'javax.servlet:javax.servlet-api:3.1.0' testCompile "ch.qos.logback:logback-classic:1.1.2" } diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/gradle.properties b/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/gradle.properties index 71fcc0538e..c484dce638 100644 --- a/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/gradle.properties +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/gradle.properties @@ -1,4 +1,4 @@ groupId=com.ofg jacksonMapper=1.9.13 restAssuredVersion=2.4.0 -springVersion=4.1.4.RELEASE \ No newline at end of file +springVersion=4.1.7.RELEASE \ No newline at end of file diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/build.gradle b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/build.gradle index 0ae605c532..ee3e5ea354 100644 --- a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/build.gradle +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/build.gradle @@ -9,8 +9,8 @@ buildscript { } ext { - spockVersion = '0.7-groovy-2.0' restAssuredVersion = '2.4.0' + spockVersion = '1.0-groovy-2.4' accurestStubsBaseDirectory = 'src/test/resources/stubs' } @@ -18,19 +18,16 @@ ext { subprojects { apply plugin: 'groovy' - repositories { mavenCentral() mavenLocal() } dependencies { - testCompile "org.codehaus.groovy:groovy-all:2.3.7" + testCompile 'org.codehaus.groovy:groovy-all:2.4.4' testCompile "org.spockframework:spock-core:$spockVersion" - testCompile("junit:junit:4.12") - testCompile('com.github.tomakehurst:wiremock:1.52') { - exclude group: 'org.mortbay.jetty', module: 'servlet-api' - } + testCompile 'junit:junit:4.12' + testCompile 'com.github.tomakehurst:wiremock:2.0.4-beta' } } @@ -59,10 +56,10 @@ configure([project(':fraudDetectionService'), project(':loanApplicationService') dependencies { compile "javax.ws.rs:javax.ws.rs-api:2.0.1" compile 'org.glassfish.jersey.containers:jersey-container-jetty-http:2.15' - compile('org.springframework.boot:spring-boot-starter-jersey:1.2.5.RELEASE') { + compile('org.springframework.boot:spring-boot-starter-jersey:1.2.6.RELEASE') { exclude module: "spring-boot-starter-tomcat" } - compile 'org.springframework.boot:spring-boot-starter-jetty:1.2.5.RELEASE' + compile 'org.springframework.boot:spring-boot-starter-jetty:1.2.6.RELEASE' testRuntime "org.spockframework:spock-spring:$spockVersion" diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/build.gradle b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/build.gradle index aa4e17339c..7a88858a51 100644 --- a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/build.gradle +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/build.gradle @@ -9,8 +9,8 @@ buildscript { } ext { - spockVersion = '0.7-groovy-2.0' restAssuredVersion = '2.4.0' + spockVersion = '1.0-groovy-2.4' accurestStubsBaseDirectory = 'src/test/resources/stubs' } @@ -24,12 +24,10 @@ subprojects { } dependencies { - testCompile "org.codehaus.groovy:groovy-all:2.3.7" + testCompile "org.codehaus.groovy:groovy-all:2.4.4" testCompile "org.spockframework:spock-core:$spockVersion" testCompile("junit:junit:4.12") - testCompile('com.github.tomakehurst:wiremock:1.52') { - exclude group: 'org.mortbay.jetty', module: 'servlet-api' - } + testCompile "com.github.tomakehurst:wiremock:2.0.4-beta" } } diff --git a/build.gradle b/build.gradle index fda2787073..42d265f044 100644 --- a/build.gradle +++ b/build.gradle @@ -18,8 +18,8 @@ scmVersion { releaseCommitMessage { version, position -> "Release version: ${version}\n\n[ci skip]" } hooks { pre "fileUpdate", [file : "README.md", - pattern : { v, p -> /'io\.codearte\.accurest:accurest-gradle-plugin:.*'/ }, - replacement: { v, p -> "'io.codearte.accurest:accurest-gradle-plugin:$v'" }] + pattern : { v, p -> /'io\.codearte\.accurest:accurest-gradle-plugin:.*'/ }, + replacement: { v, p -> "'io.codearte.accurest:accurest-gradle-plugin:$v'" }] } } @@ -71,7 +71,7 @@ project(':accurest-core') { compile 'com.google.code.gson:gson:2.3.1' compile 'com.fasterxml.jackson.core:jackson-databind:2.4.5' compile 'asm:asm:3.3.1' - compile 'com.github.tomakehurst:wiremock:2.0.4-beta' + compile "com.github.tomakehurst:wiremock:$wiremockVersion" testCompile 'cglib:cglib-nodep:2.2' testCompile 'org.objenesis:objenesis:2.1' testCompile project(':accurest-testing-utils') @@ -93,7 +93,7 @@ project(':accurest-converters') { compile 'org.apache.commons:commons-lang3:[3.0,)' compile 'commons-io:commons-io:[2.0,)' compile 'dk.brics.automaton:automaton:1.11-8' // needed for Xeger - testCompile 'com.github.tomakehurst:wiremock:2.0.2-beta' + testCompile "com.github.tomakehurst:wiremock:$wiremockVersion" testCompile 'org.hamcrest:hamcrest-all:1.3' } } @@ -103,7 +103,6 @@ project(':accurest-gradle-plugin') { compile project(':accurest-core') compile project(':accurest-converters') compile gradleApi() - testCompile('com.netflix.nebula:nebula-test:2.2.1') { exclude(group: 'org.spockframework') } diff --git a/gradle.properties b/gradle.properties index fe3cdf2fd8..8ab8e8bf7b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,4 @@ nexusUsername = -nexusPassword = \ No newline at end of file +nexusPassword = + +wiremockVersion = 2.0.4-beta From bf43ca3c7429b7ed652e9557bc4964037422bad3 Mon Sep 17 00:00:00 2001 From: Mariusz Smykula Date: Fri, 2 Oct 2015 17:07:00 +0200 Subject: [PATCH 095/119] Ugly fix for escaping bug #143 #126 --- .../accurest/util/ContentUtils.groovy | 4 +-- .../MockMvcSpockMethodBuilderSpec.groovy | 36 ++++++++++++++++--- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy index a7e66f9ec3..8aae7a832f 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy @@ -131,7 +131,7 @@ class ContentUtils { bodyAsValue.values.collect { transformJSONStringValue(it, valueProvider) } as String[], bodyAsValue.strings.clone() as String[] ) - def parsedJson = new JsonSlurper().parseText(transformedString.toString()) + def parsedJson = new JsonSlurper().parseText(transformedString.toString().replace('\\', '\\\\')) return convertAllTemporaryRegexPlaceholdersBackToPatterns(parsedJson) } @@ -166,7 +166,7 @@ class ContentUtils { MapConverter.transformValues(parsedJson, { Object value -> if (value instanceof String) { String string = (String) value - Matcher matcher = TEMPORARY_PATTERN_HOLDER.matcher(string) + Matcher matcher = TEMPORARY_PATTERN_HOLDER.matcher(string.trim()) if (matcher.matches()) { List val = matcher[0] as List String pattern = val[1] diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy index e3e4120e0d..9267dfb559 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy @@ -238,6 +238,35 @@ class MockMvcSpockMethodBuilderSpec extends Specification implements WireMockStu } def "should generate regex assertions for string objects in response body"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "GET" + url "test" + } + response { + status 200 + body("""{"property1":"a","property2":"${value(client('123'), server(regex('[0-9]{3}')))}"}""") + headers { + header('Content-Type': 'application/json') + + } + + } + } + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains("\$[?(@.property2 =~ /[0-9]{3}/)]") + blockBuilder.toString().contains("\$[?(@.property1 == 'a')]") + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) + } + + @Issue(["#126", "#143"]) + def "should generate escaped regex assertions for string objects in response body"() { given: GroovyDsl contractDsl = GroovyDsl.make { request { @@ -246,12 +275,10 @@ class MockMvcSpockMethodBuilderSpec extends Specification implements WireMockStu } response { status 200 - body("""{"property1":"a","property2":"${value(client('123'), server(regex('[0-9]{3}')))}"}""") + body("""{"property":" ${value(client('123'), server(regex('\\d+')))}"}""") headers { header('Content-Type': 'application/json') - } - } } MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) @@ -259,8 +286,7 @@ class MockMvcSpockMethodBuilderSpec extends Specification implements WireMockStu when: builder.appendTo(blockBuilder) then: - blockBuilder.toString().contains("\$[?(@.property2 =~ /[0-9]{3}/)]") - blockBuilder.toString().contains("\$[?(@.property1 == 'a')]") + blockBuilder.toString().contains("\$[?(@.property =~ /\\d+/)]") and: stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) } From f21e4512ab470a3ce4b855178930125442e948c1 Mon Sep 17 00:00:00 2001 From: Mariusz Smykula Date: Fri, 2 Oct 2015 20:33:14 +0200 Subject: [PATCH 096/119] size assertion causes invalid specs for some arrays #151 --- .../main/groovy/io/codearte/accurest/util/JsonPathEntry.groovy | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonPathEntry.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonPathEntry.groovy index 0e0f928f22..be1638027e 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonPathEntry.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonPathEntry.groovy @@ -17,8 +17,7 @@ class JsonPathEntry { if (optionalSuffix) { return ["!${parsedJsonVariable}.read('''${jsonPath}''', JSONArray).empty"] } else if (traversesOverCollections()) { - return ["${parsedJsonVariable}.read('''${jsonPath}''', JSONArray).size() == 1", - "${parsedJsonVariable}.read('''${jsonPath}''', JSONArray).get(0) ${operator()} ${potentiallyWrappedWithQuotesValue()}"] + return ["${parsedJsonVariable}.read('''${jsonPath}''', JSONArray).get(0) ${operator()} ${potentiallyWrappedWithQuotesValue()}"] } return ["${parsedJsonVariable}.read('''${jsonPath}''') ${operator()} ${potentiallyWrappedWithQuotesValue()}"] } From 450b785501a4dc2b3ea562f03a0f2a98b8c95329 Mon Sep 17 00:00:00 2001 From: Mariusz Smykula Date: Fri, 2 Oct 2015 21:28:32 +0200 Subject: [PATCH 097/119] Release version: 0.9.3 [ci skip] From 390c1ae2fbf10c25c91c04a1325b2ab193b46eea Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Sat, 3 Oct 2015 10:49:43 +0200 Subject: [PATCH 098/119] [#72] Make execution method work with GString, closes #71, closes #72, closes #101 --- .../accurest/util/ContentUtils.groovy | 31 ++++++++++--- .../MockMvcSpockMethodBuilderSpec.groovy | 43 +++++++++++++++++++ 2 files changed, 67 insertions(+), 7 deletions(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy index 8aae7a832f..87b940c663 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy @@ -5,6 +5,7 @@ import groovy.json.JsonSlurper import groovy.transform.TypeChecked import groovy.util.logging.Slf4j 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 org.codehaus.groovy.runtime.GStringImpl @@ -24,7 +25,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 String JSON_VALUE_PATTERN_FOR_REGEX = 'REGEXP>>%s<<' + private static final String JSON_VALUE_PATTERN_FOR_EXECUTION = '"EXECUTION>>%s<<"' /** * Due to the fact that we allow users to have a body with GString and different values inside @@ -154,6 +157,10 @@ class ContentUtils { return String.format(JSON_VALUE_PATTERN_FOR_REGEX, pattern.pattern()) } + private static String transformJSONStringValue(ExecutionProperty property, Closure valueProvider) { + return String.format(JSON_VALUE_PATTERN_FOR_EXECUTION, property.executionCommand) + } + private static String transformXMLStringValue(Object obj, Closure valueProvider) { return escapeXml11(obj.toString()) } @@ -166,18 +173,28 @@ class ContentUtils { MapConverter.transformValues(parsedJson, { Object value -> if (value instanceof String) { String string = (String) value - 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 value + return returnParsedObject(string) } return value }) } + 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) + } + 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 string + } + public static ContentType recognizeContentTypeFromHeader(Headers headers) { String content = headers?.entries.find { it.name == "Content-Type" } ?.clientValue?.toString() if (content?.endsWith("json")) { diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy index 9267dfb559..7c16b662d7 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy @@ -551,4 +551,47 @@ class MockMvcSpockMethodBuilderSpec extends Specification implements WireMockStu then: spockTest.contains('''$[?(@.message =~ /User not found by email = \\\\[[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,4}\\\\]/)]''') } + + @Issue('72') + def "should make the execute method work"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method """PUT""" + url """/fraudcheck""" + body(""" + { + "clientPesel":"${value(client(regex('[0-9]{10}')), server('1234567890'))}", + "loanAmount":123.123 + } + """ + ) + headers { + header("""Content-Type""", """application/vnd.fraud.v1+json""") + + } + + } + response { + status 200 + body( """{ + "fraudCheckStatus": "OK", + "rejectionReason": ${value(client(null), server(execute('assertThatRejectionReasonIsNull($it)')))} +}""") + headers { + header('Content-Type': 'application/vnd.fraud.v1+json') + + } + + } + + } + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + def spockTest = blockBuilder.toString() + then: + spockTest.contains('''assertThatRejectionReasonIsNull(parsedJson.read('$.rejectionReason'))''') + } } From e84ceedd582a9a0b2e6370a586c1f4c2ac7f2365 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Mon, 5 Oct 2015 21:21:02 +0200 Subject: [PATCH 099/119] [#72] Added explanation to matchers --- .../accurest/util/ContentUtils.groovy | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy index 87b940c663..c18a918c8b 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy @@ -179,6 +179,30 @@ class ContentUtils { }) } + /** + *

+ * If you wonder why there is val[1] without null-check then take a look at this: + *

+ *

+ * Example: + *

+ *

+ * Our string equals: {@code EXECUTION>>assertThatRejectionReasonIsNull($it)<<} + * The matcher matches this group with the pattern {@code EXECUTION>>(.*)<<} + *

+ *

+ * So {@code executionMatcher[0]} returns 2 elements: + *

    + *
  • index0: EXECUTION>>assertThatRejectionReasonIsNull($it)<<
  • + *
  • index1: assertThatRejectionReasonIsNull($it)<<
  • + *
+ *

+ *

+ * Thus one can safely write {@code executionMatcher[0][1]} to retrieve the matched group + *

+ * @param string to match the regexps against + * @return object converted from temporary holders + */ static Object returnParsedObject(String string) { Matcher matcher = TEMPORARY_PATTERN_HOLDER.matcher(string.trim()) if (matcher.matches()) { From 9550a9ff08f99cdfcd3d51d6a12c929252135b89 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Sun, 4 Oct 2015 22:16:25 +0200 Subject: [PATCH 100/119] [#42] Optional parameter --- .../accurest/dsl/internal/Common.groovy | 22 +++-- .../accurest/dsl/internal/Optional.groovy | 7 ++ .../accurest/util/ContentUtils.groovy | 13 ++- .../util/JsonToJsonPathsConverter.groovy | 2 +- .../MockMvcSpockMethodBuilderSpec.groovy | 84 +++++++++++++++++++ 5 files changed, 121 insertions(+), 7 deletions(-) create mode 100644 accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Optional.groovy diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Common.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Common.groovy index 6cb78b67ba..531dbec5ce 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Common.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Common.groovy @@ -16,16 +16,20 @@ class Common { @Delegate private final RegexPatterns regexPatterns = new RegexPatterns() Map convertObjectsToDslProperties(Map body) { - return body.collectEntries { + return (body.collectEntries { Map.Entry entry -> [(entry.key): toDslProperty(entry.value)] - } as Map + } as Map).findAll { + !(it.value.clientValue instanceof Optional || it.value.serverValue instanceof Optional) + } } - List convertObjectsToDslProperties(List body) { - return body.collect { + Collection convertObjectsToDslProperties(List body) { + return (body.collect { Object element -> toDslProperty(element) - } as List + } as List).findAll { + !(it instanceof Optional) + } } DslProperty toDslProperty(Object property) { @@ -53,6 +57,10 @@ class Common { return new DslProperty(client.clientValue, server.serverValue) } + DslProperty value(Object value) { + return new DslProperty(value) + } + DslProperty value(ServerDslProperty server, ClientDslProperty client) { assertThatSidesMatch(client.clientValue, server.serverValue) return new DslProperty(client.clientValue, server.serverValue) @@ -90,6 +98,10 @@ class Common { return new ServerDslProperty(serverValue) } + Optional optional() { + return new Optional() + } + void assertThatSidesMatch(Pattern pattern, String value) { assert value ==~ pattern } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Optional.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Optional.groovy new file mode 100644 index 0000000000..02e7a16323 --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Optional.groovy @@ -0,0 +1,7 @@ +package io.codearte.accurest.dsl.internal + +/** + * Marker class to show that an element of message is optional + */ +class Optional { +} diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy index c18a918c8b..c5261b43be 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy @@ -8,6 +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 org.codehaus.groovy.runtime.GStringImpl import java.util.regex.Matcher @@ -24,9 +25,11 @@ class ContentUtils { it instanceof DslProperty ? it.clientValue : it } - private static final Pattern TEMPORARY_PATTERN_HOLDER = Pattern.compile('REGEXP>>(.*)<<') + private static final Pattern TEMPORARY_PATTERN_HOLDER = Pattern.compile('.*REGEXP>>(.*)<<.*') private static final Pattern TEMPORARY_EXECUTION_PATTERN_HOLDER = Pattern.compile('EXECUTION>>(.*)<<') 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_EXECUTION = '"EXECUTION>>%s<<"' /** @@ -157,6 +160,10 @@ 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(ExecutionProperty property, Closure valueProvider) { return String.format(JSON_VALUE_PATTERN_FOR_EXECUTION, property.executionCommand) } @@ -216,6 +223,10 @@ class ContentUtils { String pattern = val[1] return new ExecutionProperty(pattern) } + Matcher optionalMatcher = OPTIONAL_PATTERN_HOLDER.matcher(string.trim()) + if (optionalMatcher.matches()) { + return new Optional() + } return string } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonToJsonPathsConverter.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonToJsonPathsConverter.groovy index 32affecef2..db8f264e30 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonToJsonPathsConverter.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonToJsonPathsConverter.groovy @@ -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) { + if (value instanceof ExecutionProperty || value instanceof io.codearte.accurest.dsl.internal.Optional) { return } JsonPathEntry entry = getValueToInsert(key, value) diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy index 7c16b662d7..2eb45c0475 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy @@ -552,6 +552,90 @@ class MockMvcSpockMethodBuilderSpec extends Specification implements WireMockStu spockTest.contains('''$[?(@.message =~ /User not found by email = \\\\[[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,4}\\\\]/)]''') } + def "should omit an optional field from body resolution"() { + 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('''REGEXP''') + !spockTest.contains('''OPTIONAL''') + !spockTest.contains('''Optional''') + } + + 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' + } + body( + """ { + "email" : "${value(optional())}", + "callback_url" : "${value(client(regex(hostname())), server('http://partners.com'))}" + } + """ + ) + } + response { + status 404 + headers { + header 'Content-Type': 'application/json' + } + body( + """ { + "code" : "${value(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(\'\'\'$[?(@.code''') + !spockTest.contains('''REGEXP''') + !spockTest.contains('''OPTIONAL''') + !spockTest.contains('''Optional''') + } + @Issue('72') def "should make the execute method work"() { given: From bb73cdd4b0de3ee4b756c10bdad0ba4320aeb4f5 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Sun, 4 Oct 2015 22:37:49 +0200 Subject: [PATCH 101/119] [#42] Fixed wiremock stubs generation --- .../accurest/util/MapConverter.groovy | 3 + .../MockMvcSpockMethodBuilderSpec.groovy | 2 + .../accurest/dsl/WireMockGroovyDslSpec.groovy | 122 ++++++++++++++++++ 3 files changed, 127 insertions(+) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/util/MapConverter.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/MapConverter.groovy index ca0717ba68..c4a6eed44b 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/util/MapConverter.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/MapConverter.groovy @@ -2,6 +2,7 @@ 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 @@ -40,6 +41,8 @@ class MapConverter { return map.collectEntries { key, value -> [key, transformValues(value, closure)] + }.findAll { + !(it.value instanceof Optional) } } diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy index 2eb45c0475..f3ee4bd99a 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy @@ -552,6 +552,7 @@ class MockMvcSpockMethodBuilderSpec extends Specification implements WireMockStu spockTest.contains('''$[?(@.message =~ /User not found by email = \\\\[[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,4}\\\\]/)]''') } + @Issue('42') def "should omit an optional field from body resolution"() { given: GroovyDsl contractDsl = GroovyDsl.make { @@ -591,6 +592,7 @@ class MockMvcSpockMethodBuilderSpec extends Specification implements WireMockStu !spockTest.contains('''Optional''') } + @Issue('42') def "should omit an optional field from body resolution with GString"() { given: GroovyDsl contractDsl = GroovyDsl.make { diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy index 053d33217b..cbceb49f80 100755 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy @@ -1293,6 +1293,128 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie stubMappingIsValidWireMockStub(wireMockStub) } + @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' + } + body( + """ { + "email" : "${value(optional())}", + "callback_url" : "${value(client(regex(hostname())), server('http://partners.com'))}" + } + """ + ) + } + response { + status 404 + headers { + header 'Content-Type': 'application/json' + } + body( + """ { + "code" : "${value(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) + } + + @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) { new JsonBuilder(value).toPrettyString() } From b0bb4cb75fdbd12de4237a12361d23aea1e3c239 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Mon, 5 Oct 2015 22:38:12 +0200 Subject: [PATCH 102/119] [#42] Modified the optional functionality --- .../accurest/dsl/internal/Common.groovy | 18 +- .../accurest/dsl/internal/Optional.groovy | 7 - .../dsl/internal/OptionalProperty.groovy | 13 ++ .../accurest/dsl/internal/Request.groovy | 5 +- .../accurest/dsl/internal/Response.groovy | 3 + .../accurest/util/ContentUtils.groovy | 28 +-- .../util/JsonToJsonPathsConverter.groovy | 14 +- .../accurest/util/MapConverter.groovy | 5 - .../MockMvcSpockMethodBuilderSpec.groovy | 119 +++++------ .../accurest/dsl/WireMockGroovyDslSpec.groovy | 190 ++++++++---------- .../accurest/dsl/WireMockStubVerifier.groovy | 2 +- 11 files changed, 190 insertions(+), 214 deletions(-) delete mode 100644 accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Optional.groovy create mode 100644 accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/OptionalProperty.groovy diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Common.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Common.groovy index 531dbec5ce..c96ff4b2d4 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Common.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Common.groovy @@ -16,20 +16,16 @@ class Common { @Delegate private final RegexPatterns regexPatterns = new RegexPatterns() Map convertObjectsToDslProperties(Map body) { - return (body.collectEntries { + return body.collectEntries { Map.Entry entry -> [(entry.key): toDslProperty(entry.value)] - } as Map).findAll { - !(it.value.clientValue instanceof Optional || it.value.serverValue instanceof Optional) - } + } as Map } 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) { diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Optional.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Optional.groovy deleted file mode 100644 index 02e7a16323..0000000000 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Optional.groovy +++ /dev/null @@ -1,7 +0,0 @@ -package io.codearte.accurest.dsl.internal - -/** - * Marker class to show that an element of message is optional - */ -class Optional { -} diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/OptionalProperty.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/OptionalProperty.groovy new file mode 100644 index 0000000000..877c31c661 --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/OptionalProperty.groovy @@ -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)?" + } +} diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Request.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Request.groovy index 9be791488a..9adf7048a8 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Request.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Request.groovy @@ -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 diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Response.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Response.groovy index 3715f0c243..b2546cd5c8 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Response.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Response.groovy @@ -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 diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy index c5261b43be..1b370b7126 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy @@ -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")) { diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonToJsonPathsConverter.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonToJsonPathsConverter.groovy index db8f264e30..319e7ab660 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonToJsonPathsConverter.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonToJsonPathsConverter.groovy @@ -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'" } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/util/MapConverter.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/MapConverter.groovy index c4a6eed44b..22896d8308 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/util/MapConverter.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/MapConverter.groovy @@ -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) } } diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy index f3ee4bd99a..44cb4e235f 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy @@ -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') diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy index cbceb49f80..7502ada771 100755 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy @@ -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) { diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockStubVerifier.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockStubVerifier.groovy index 241380062f..537fb78ea2 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockStubVerifier.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockStubVerifier.groovy @@ -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') } } From 10835c1fcbe116c03301183892cf5f1bde70c526 Mon Sep 17 00:00:00 2001 From: Mariusz Smykula Date: Tue, 6 Oct 2015 13:06:11 +0200 Subject: [PATCH 103/119] wiremock update to 2.0.5-beta and cleanup --- .../plugin/AccurestGradlePlugin.groovy | 2 +- .../functionalTest/bootSimple/build.gradle | 7 ++---- .../sampleJerseyProject/build.gradle | 22 ++++++++++--------- .../functionalTest/sampleProject/build.gradle | 9 ++++---- gradle.properties | 2 +- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/AccurestGradlePlugin.groovy b/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/AccurestGradlePlugin.groovy index efd3f76e92..11a86d33c9 100644 --- a/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/AccurestGradlePlugin.groovy +++ b/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/AccurestGradlePlugin.groovy @@ -30,7 +30,7 @@ class AccurestGradlePlugin implements Plugin { createGenerateTestsTask(extension) createAndConfigureGenerateWireMockClientStubsFromDslTask(extension) deprecatedCreateAndConfigureGenerateWiremockClientStubsFromDslTask() - project.dependencies.add("testCompile", "com.github.tomakehurst:wiremock:2.0.4-beta") + project.dependencies.add("testCompile", "com.github.tomakehurst:wiremock:2.0.5-beta") project.afterEvaluate { def hasIdea = project.plugins.findPlugin(IDEA_PLUGIN_CLASS) diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/build.gradle b/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/build.gradle index e392e694c3..e7565e3efe 100644 --- a/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/build.gradle +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/build.gradle @@ -34,17 +34,14 @@ repositories { dependencies { compile "org.springframework:spring-web:$springVersion" compile "org.springframework:spring-context-support:$springVersion" - compile "org.codehaus.groovy:groovy-all:2.4.4" + compile "org.codehaus.groovy:groovy-all:2.4.5" compile 'com.fasterxml.jackson.core:jackson-databind:2.4.4' - compile "org.codehaus.jackson:jackson-mapper-asl:$jacksonMapper" - compile "org.codehaus.jackson:jackson-core-asl:$jacksonMapper" compile 'com.jayway.jsonpath:json-path-assert:2.0.0' - testCompile "com.github.tomakehurst:wiremock:2.0.4-beta" + testCompile "com.github.tomakehurst:wiremock:2.0.5-beta" testCompile "org.spockframework:spock-spring:1.0-groovy-2.4" testCompile "com.jayway.restassured:rest-assured:$restAssuredVersion" testCompile "com.jayway.restassured:spring-mock-mvc:$restAssuredVersion" - testCompile 'javax.servlet:javax.servlet-api:3.1.0' testCompile "ch.qos.logback:logback-classic:1.1.2" } diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/build.gradle b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/build.gradle index ee3e5ea354..f33b17b5a6 100644 --- a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/build.gradle +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/build.gradle @@ -4,13 +4,14 @@ buildscript { mavenCentral() } dependencies { - classpath "org.springframework.boot:spring-boot-gradle-plugin:1.2.1.RELEASE" + classpath "org.springframework.boot:spring-boot-gradle-plugin:1.2.6.RELEASE" } } ext { - restAssuredVersion = '2.4.0' + restAssuredVersion = '2.5.0' spockVersion = '1.0-groovy-2.4' + wiremockVersion = '2.0.5-beta' accurestStubsBaseDirectory = 'src/test/resources/stubs' } @@ -24,10 +25,10 @@ subprojects { } dependencies { - testCompile 'org.codehaus.groovy:groovy-all:2.4.4' + testCompile 'org.codehaus.groovy:groovy-all:2.4.5' testCompile "org.spockframework:spock-core:$spockVersion" testCompile 'junit:junit:4.12' - testCompile 'com.github.tomakehurst:wiremock:2.0.4-beta' + testCompile "com.github.tomakehurst:wiremock:$wiremockVersion" } } @@ -53,18 +54,19 @@ configure([project(':fraudDetectionService'), project(':loanApplicationService') version = '0.0.1' } + configurations { + compile.exclude module: "spring-boot-starter-tomcat" + } + dependencies { - compile "javax.ws.rs:javax.ws.rs-api:2.0.1" compile 'org.glassfish.jersey.containers:jersey-container-jetty-http:2.15' - compile('org.springframework.boot:spring-boot-starter-jersey:1.2.6.RELEASE') { - exclude module: "spring-boot-starter-tomcat" - } - compile 'org.springframework.boot:spring-boot-starter-jetty:1.2.6.RELEASE' + compile 'org.springframework.boot:spring-boot-starter-jersey' + compile 'org.springframework.boot:spring-boot-starter-jetty' testRuntime "org.spockframework:spock-spring:$spockVersion" compile 'org.glassfish.jersey.connectors:jersey-apache-connector:2.15' - testCompile 'org.springframework:spring-test:4.1.7.RELEASE' + testCompile 'org.springframework:spring-test' } task cleanup(type: Delete) { diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/build.gradle b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/build.gradle index 7a88858a51..25ca064fc4 100644 --- a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/build.gradle +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/build.gradle @@ -4,13 +4,14 @@ buildscript { mavenLocal() } dependencies { - classpath("org.springframework.boot:spring-boot-gradle-plugin:1.2.1.RELEASE") + classpath("org.springframework.boot:spring-boot-gradle-plugin:1.2.6.RELEASE") } } ext { - restAssuredVersion = '2.4.0' + restAssuredVersion = '2.5.0' spockVersion = '1.0-groovy-2.4' + wiremockVersion = '2.0.5-beta' accurestStubsBaseDirectory = 'src/test/resources/stubs' } @@ -24,10 +25,10 @@ subprojects { } dependencies { - testCompile "org.codehaus.groovy:groovy-all:2.4.4" + testCompile "org.codehaus.groovy:groovy-all:2.4.5" testCompile "org.spockframework:spock-core:$spockVersion" testCompile("junit:junit:4.12") - testCompile "com.github.tomakehurst:wiremock:2.0.4-beta" + testCompile "com.github.tomakehurst:wiremock:$wiremockVersion" } } diff --git a/gradle.properties b/gradle.properties index 8ab8e8bf7b..27a71580f7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ nexusUsername = nexusPassword = -wiremockVersion = 2.0.4-beta +wiremockVersion = 2.0.5-beta From ed1cd6eab31271bacb1d2e2a992f8f015953b710 Mon Sep 17 00:00:00 2001 From: Denis Stepanov Date: Tue, 6 Oct 2015 17:36:10 +0200 Subject: [PATCH 104/119] Fix encoding issue --- .../src/main/groovy/io/codearte/accurest/TestGenerator.groovy | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/TestGenerator.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/TestGenerator.groovy index 63e2047217..78e414e929 100755 --- a/accurest-core/src/main/groovy/io/codearte/accurest/TestGenerator.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/TestGenerator.groovy @@ -4,6 +4,7 @@ import io.codearte.accurest.config.AccurestConfigProperties import org.apache.commons.io.FilenameUtils import org.codehaus.plexus.util.DirectoryScanner +import java.nio.charset.StandardCharsets import java.util.concurrent.atomic.AtomicInteger import static io.codearte.accurest.util.NamesUtil.afterLast @@ -62,7 +63,7 @@ class TestGenerator { } if (filesToClass.size()) { def className = afterLast(includedDirectoryRelativePath, File.separator) + configProperties.targetFramework.classNameSuffix - def classBytes = generator.buildClass(filesToClass, className, packageNameForClass).bytes + def classBytes = generator.buildClass(filesToClass, className, packageNameForClass).getBytes(StandardCharsets.UTF_8) saver.saveClassFile(className, packageNameForClass, classBytes) counter.incrementAndGet() } From 1ab57580a9b2761b0b6ecce813f754f6f8ec2b3d Mon Sep 17 00:00:00 2001 From: Mariusz Smykula Date: Tue, 6 Oct 2015 20:28:07 +0200 Subject: [PATCH 105/119] Release version: 0.9.4 [ci skip] From cbb39dbad6ddbaf67b432819f4042bac904ebb3c Mon Sep 17 00:00:00 2001 From: Denis Stepanov Date: Wed, 7 Oct 2015 11:11:15 +0200 Subject: [PATCH 106/119] Fix encoding issue --- .../groovy/io/codearte/accurest/builder/MethodBuilder.groovy | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/MethodBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/MethodBuilder.groovy index c4e8da6d04..55a0f66f36 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/builder/MethodBuilder.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/MethodBuilder.groovy @@ -6,6 +6,7 @@ import io.codearte.accurest.config.TestFramework import io.codearte.accurest.config.TestMode import io.codearte.accurest.dsl.GroovyDsl import io.codearte.accurest.util.NamesUtil +import org.codehaus.groovy.control.CompilerConfiguration /** * @author Jakub Kubrynski @@ -25,7 +26,7 @@ class MethodBuilder { static MethodBuilder createTestMethod(File stubsFile, AccurestConfigProperties configProperties) { log.debug("Stub content from file [${stubsFile.text}]") - GroovyDsl stubContent = new GroovyShell(this.classLoader).evaluate(stubsFile) + GroovyDsl stubContent = new GroovyShell(this.classLoader, new Binding(), new CompilerConfiguration(sourceEncoding:'UTF-8')).evaluate(stubsFile) log.debug("Stub content Groovy DSL [$stubContent]") String methodName = NamesUtil.camelCase(NamesUtil.toLastDot(NamesUtil.afterLast(stubsFile.path, File.separator))) return new MethodBuilder(methodName, stubContent, configProperties) From 9d16cc051fc3f04a71ac5e644a80198b90bb8bb7 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Wed, 7 Oct 2015 11:29:24 +0200 Subject: [PATCH 107/119] Revert "Set UTF-8 encoding when loading stubs" --- .../groovy/io/codearte/accurest/builder/MethodBuilder.groovy | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/MethodBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/MethodBuilder.groovy index 55a0f66f36..c4e8da6d04 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/builder/MethodBuilder.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/MethodBuilder.groovy @@ -6,7 +6,6 @@ import io.codearte.accurest.config.TestFramework import io.codearte.accurest.config.TestMode import io.codearte.accurest.dsl.GroovyDsl import io.codearte.accurest.util.NamesUtil -import org.codehaus.groovy.control.CompilerConfiguration /** * @author Jakub Kubrynski @@ -26,7 +25,7 @@ class MethodBuilder { static MethodBuilder createTestMethod(File stubsFile, AccurestConfigProperties configProperties) { log.debug("Stub content from file [${stubsFile.text}]") - GroovyDsl stubContent = new GroovyShell(this.classLoader, new Binding(), new CompilerConfiguration(sourceEncoding:'UTF-8')).evaluate(stubsFile) + GroovyDsl stubContent = new GroovyShell(this.classLoader).evaluate(stubsFile) log.debug("Stub content Groovy DSL [$stubContent]") String methodName = NamesUtil.camelCase(NamesUtil.toLastDot(NamesUtil.afterLast(stubsFile.path, File.separator))) return new MethodBuilder(methodName, stubContent, configProperties) From f5da5c4278984a1503fd2c287c97e4d5b81b3ea2 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Wed, 7 Oct 2015 11:32:05 +0200 Subject: [PATCH 108/119] Revert "Merge pull request #159 from Codearte/revert-158-encoding-fix2" This reverts commit a85bb25b13a3a4396a96c9015ab26feae5a73db7, reversing changes made to c7a066bb364bfd24efe29710533a39197f357e3f. --- .../groovy/io/codearte/accurest/builder/MethodBuilder.groovy | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/MethodBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/MethodBuilder.groovy index c4e8da6d04..55a0f66f36 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/builder/MethodBuilder.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/MethodBuilder.groovy @@ -6,6 +6,7 @@ import io.codearte.accurest.config.TestFramework import io.codearte.accurest.config.TestMode import io.codearte.accurest.dsl.GroovyDsl import io.codearte.accurest.util.NamesUtil +import org.codehaus.groovy.control.CompilerConfiguration /** * @author Jakub Kubrynski @@ -25,7 +26,7 @@ class MethodBuilder { static MethodBuilder createTestMethod(File stubsFile, AccurestConfigProperties configProperties) { log.debug("Stub content from file [${stubsFile.text}]") - GroovyDsl stubContent = new GroovyShell(this.classLoader).evaluate(stubsFile) + GroovyDsl stubContent = new GroovyShell(this.classLoader, new Binding(), new CompilerConfiguration(sourceEncoding:'UTF-8')).evaluate(stubsFile) log.debug("Stub content Groovy DSL [$stubContent]") String methodName = NamesUtil.camelCase(NamesUtil.toLastDot(NamesUtil.afterLast(stubsFile.path, File.separator))) return new MethodBuilder(methodName, stubContent, configProperties) From cf8ee61eb3a41499e344a7ea3e067c0538969073 Mon Sep 17 00:00:00 2001 From: Denis Stepanov Date: Thu, 8 Oct 2015 10:36:12 +0200 Subject: [PATCH 109/119] Support inner map and list --- .../dsl/internal/RegexPatterns.groovy | 11 ++++ .../accurest/util/MapConverter.groovy | 16 ++++- .../MockMvcSpockMethodBuilderSpec.groovy | 66 +++++++++++++++++++ 3 files changed, 92 insertions(+), 1 deletion(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/RegexPatterns.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/RegexPatterns.groovy index 432dee266d..0f530bbf8f 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/RegexPatterns.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/RegexPatterns.groovy @@ -7,11 +7,22 @@ import java.util.regex.Pattern @CompileStatic class RegexPatterns { + private static final Pattern TRUE_OR_FALSE = Pattern.compile(/(true|false)/) + private static final Pattern ONLY_ALPHA_UNICODE = Pattern.compile(/[\p{L}]*/) private 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])'); private static final Pattern HOSTNAME_PATTERN = Pattern.compile('((http[s]?|ftp):\\/)\\/?([^:\\/\\s]+)(:[0-9]{1,5})?'); private static final Pattern EMAIL = Pattern.compile('[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}'); private static final Pattern URL = Pattern.compile('((www\\.|(http|https|ftp|news|file)+\\:\\/\\/)[_.a-z0-9-]+\\.[a-z0-9\\/_:@=.+?,##%&~-]*[^.|\\\'|\\# |!|\\(|?|,| |>|<|;|\\)])'); + + String onlyAlphaUnicode() { + return ONLY_ALPHA_UNICODE.pattern() + } + + String anyBoolean() { + return TRUE_OR_FALSE.pattern() + } + String ipAddress() { return IP_ADDRESS.pattern() } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/util/MapConverter.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/MapConverter.groovy index 22896d8308..7087f9c1d7 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/util/MapConverter.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/MapConverter.groovy @@ -20,13 +20,27 @@ class MapConverter { return convert(json, closure) } } catch (Exception ignore) { - return closure(value) } + return extractValue(value, closure); } else if (value instanceof Map) { return convert(value as Map, closure) } else if (value instanceof List) { return value.collect({ transformValues(it, closure) }) } + return transformValue(closure, value) + } + + protected static Object transformValue(Closure closure, Object value) { + return extractValue(value, { Object val-> + Object newValue = closure(val) + if (newValue instanceof Map || newValue instanceof List || newValue instanceof String && value) { + return transformValues(newValue, closure) + } + return newValue; + }) + } + + private static extractValue(Object value, Closure closure) { try { return closure(value) } catch (Exception ignore) { diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy index 44cb4e235f..8d6bdbf343 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy @@ -7,6 +7,8 @@ import spock.lang.Issue import spock.lang.Specification import spock.lang.Unroll +import java.util.regex.Pattern + /** * @author Jakub Kubrynski */ @@ -669,4 +671,68 @@ class MockMvcSpockMethodBuilderSpec extends Specification implements WireMockStu then: spockTest.contains('''assertThatRejectionReasonIsNull(parsedJson.read('$.rejectionReason'))''') } + + def "should support inner map and list definitions"() { + given: + + Pattern PHONE_NUMBER = Pattern.compile(/[+\w]*/) + Pattern ANYSTRING = Pattern.compile(/.*/) + Pattern NUMBERS = Pattern.compile(/[\d\.]*/) + Pattern DATETIME = ANYSTRING + + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "PUT" + url "/v1/payments/e86df6f693de4b35ae648464c5b0dc09/client_data" + headers { + header('Content-Type': 'application/json') + } + body( + client: [ + first_name: $(stub(regex(onlyAlphaUnicode())), test('Denis')), + last_name: $(stub(regex(onlyAlphaUnicode())), test('FakeName')), + email: $(stub(regex(email())), test('fakemail@fakegmail.com')), + fax: $(stub(PHONE_NUMBER), test('+xx001213214')), + phone: $(stub(PHONE_NUMBER), test('2223311')), + data_of_birth: $(stub(DATETIME), test('2002-10-22T00:00:00Z')) + ], + client_id_card: [ + id: $(stub(ANYSTRING), test('ABC12345')), + date_of_issue: $(stub(ANYSTRING), test('2002-10-02T00:00:00Z')), + address: [ + street: $(stub(ANYSTRING), test('Light Street')), + city: $(stub(ANYSTRING), test('Fire')), + region: $(stub(ANYSTRING), test('Skys')), + country: $(stub(ANYSTRING), test('HG')), + zip: $(stub(NUMBERS), test('658965')) + ] + ], + incomes_and_expenses: [ + monthly_income: $(stub(NUMBERS), test('0.0')), + monthly_loan_repayments: $(stub(NUMBERS), test('100')), + monthly_living_expenses: $(stub(NUMBERS), test('22')) + ], + additional_info: [ + allow_to_contact: $(stub(optional(regex(anyBoolean()))), test('true')) + ] + ) + } + response { + status 200 + headers { + header('Content-Type': 'application/json') + } + } + } + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + def spockTest = blockBuilder.toString() + then: + spockTest.contains '"street":"Light Street"' + !spockTest.contains("clientValue") + !spockTest.contains("cursor") + } + } From 806ad6086fba7a089ff90d00abe7983bbcb4ccd2 Mon Sep 17 00:00:00 2001 From: Mariusz Smykula Date: Fri, 9 Oct 2015 12:43:01 +0200 Subject: [PATCH 110/119] Release version: 0.9.6 [ci skip] From bfd72c1275d996d7dacf4e9736438a5b272ef0fb Mon Sep 17 00:00:00 2001 From: Denis Stepanov Date: Wed, 7 Oct 2015 17:14:25 +0200 Subject: [PATCH 111/119] Fix encoding issue --- .../builder/SpockMethodBodyBuilder.groovy | 10 ++++- .../MockMvcSpockMethodBuilderSpec.groovy | 37 +++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy index 2789e52a2d..618398ad5b 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy @@ -1,5 +1,7 @@ package io.codearte.accurest.builder + import groovy.json.JsonOutput +import groovy.json.StringEscapeUtils import groovy.transform.PackageScope import groovy.transform.TypeChecked import io.codearte.accurest.dsl.GroovyDsl @@ -117,7 +119,13 @@ abstract class SpockMethodBodyBuilder { protected String getBodyAsString() { Object bodyValue = extractServerValueFromBody(request.body.serverValue) - return trimRepeatedQuotes(new JsonOutput().toJson(bodyValue)) + String json = new JsonOutput().toJson(bodyValue) + json = convertUnicodeEscapes(json) + return trimRepeatedQuotes(json) + } + + protected String convertUnicodeEscapes(String json) { + return StringEscapeUtils.unescapeJavaScript(json) } protected String trimRepeatedQuotes(String toTrim) { diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy index 44cb4e235f..fce1bdff84 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy @@ -7,6 +7,8 @@ import spock.lang.Issue import spock.lang.Specification import spock.lang.Unroll +import java.util.regex.Pattern + /** * @author Jakub Kubrynski */ @@ -669,4 +671,39 @@ class MockMvcSpockMethodBuilderSpec extends Specification implements WireMockStu then: spockTest.contains('''assertThatRejectionReasonIsNull(parsedJson.read('$.rejectionReason'))''') } + + def "shouldn't generate unicode escape characters"() { + given: + Pattern ONLY_ALPHA_UNICODE = Pattern.compile(/[\p{L}]*/) + + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "PUT" + url "/v1/payments/e86df6f693de4b35ae648464c5b0dc09/енев" + headers { + header('Content-Type': 'application/json') + } + body( + client: [ + first_name: $(stub(ONLY_ALPHA_UNICODE), test('Пенева')), + last_name : $(stub(ONLY_ALPHA_UNICODE), test('Пенева')) + ] + ) + } + response { + status 200 + headers { + header('Content-Type': 'application/json') + } + } + } + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + def spockTest = blockBuilder.toString() + then: + !spockTest.contains("\\u041f") + } + } From 25ce3d7f71e273d705bc74c88f7d5a2a225fb9d9 Mon Sep 17 00:00:00 2001 From: Denis Stepanov Date: Tue, 13 Oct 2015 14:57:08 +0200 Subject: [PATCH 112/119] Add UTF-8 as Groovy DSL file format --- .../codearte/accurest/wiremock/DslToWireMockConverter.groovy | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/DslToWireMockConverter.groovy b/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/DslToWireMockConverter.groovy index 9163724610..89b2b381ce 100644 --- a/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/DslToWireMockConverter.groovy +++ b/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/DslToWireMockConverter.groovy @@ -2,6 +2,7 @@ package io.codearte.accurest.wiremock import groovy.transform.CompileStatic import io.codearte.accurest.dsl.GroovyDsl +import org.codehaus.groovy.control.CompilerConfiguration @CompileStatic abstract class DslToWireMockConverter implements SingleFileConverter { @@ -17,6 +18,6 @@ abstract class DslToWireMockConverter implements SingleFileConverter { } protected GroovyDsl createGroovyDSLfromStringContent(String groovyDslAsString) { - return (GroovyDsl) new GroovyShell(this.class.classLoader).evaluate("$groovyDslAsString") + return (GroovyDsl) new GroovyShell(this.class.classLoader, new Binding(), new CompilerConfiguration(sourceEncoding:'UTF-8')).evaluate("$groovyDslAsString") } } From 1d6fe2291056fe2849c4a2ea653cc86efab8fba2 Mon Sep 17 00:00:00 2001 From: Mariusz Smykula Date: Tue, 13 Oct 2015 15:55:57 +0200 Subject: [PATCH 113/119] Release version: 0.9.7 [ci skip] From be20569e4aca3383cfa88c22b91ac00e943456bd Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Thu, 15 Oct 2015 10:23:26 +0200 Subject: [PATCH 114/119] Updated readme with requirements for wiremock --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index b079b674a9..923a398c25 100644 --- a/README.md +++ b/README.md @@ -13,3 +13,9 @@ generating Spock's acceptance tests for the server - to verify if your API imple 2. moving TDD to an architecture level. For more information please go to the [Wiki](https://github.com/Codearte/accurest/wiki/1.-Introduction) + +## Requirements + +### Wiremock + +In order to use Accurest with Wiremock you have to have __Wiremock in version at least 2.0.0-beta__ . Of course the higher the better :) From e7a69c3d5b0e2d7b64ed60cd7b592c8fadedbb42 Mon Sep 17 00:00:00 2001 From: Denis Stepanov Date: Thu, 15 Oct 2015 10:04:12 +0200 Subject: [PATCH 115/119] Set UTF-8 default encoding --- .../accurest/wiremock/RecursiveFilesConverter.groovy | 5 +++-- .../accurest/wiremock/WireMockToDslConverter.groovy | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/RecursiveFilesConverter.groovy b/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/RecursiveFilesConverter.groovy index 0f74866305..8f71f8955b 100644 --- a/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/RecursiveFilesConverter.groovy +++ b/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/RecursiveFilesConverter.groovy @@ -4,6 +4,7 @@ import groovy.io.FileType import groovy.transform.CompileStatic import groovy.util.logging.Slf4j +import java.nio.charset.StandardCharsets import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths @@ -28,10 +29,10 @@ class RecursiveFilesConverter { if (!singleFileConverter.canHandleFileName(sourceFile.name)) { return } - String convertedContent = singleFileConverter.convertContent(sourceFile.text) + String convertedContent = singleFileConverter.convertContent(sourceFile.getText(StandardCharsets.UTF_8.toString())) Path absoluteTargetPath = createAndReturnTargetDirectory(sourceFile) File newGroovyFile = createTargetFileWithProperName(absoluteTargetPath, sourceFile) - newGroovyFile.text = convertedContent + newGroovyFile.setText(convertedContent, StandardCharsets.UTF_8.toString()) } catch (Exception e) { throw new ConversionAccurestException("Unable to make convertion of ${sourceFile.name}", e) } diff --git a/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/WireMockToDslConverter.groovy b/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/WireMockToDslConverter.groovy index e7a2a45f97..4107d2dcdf 100644 --- a/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/WireMockToDslConverter.groovy +++ b/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/WireMockToDslConverter.groovy @@ -8,6 +8,8 @@ import groovy.xml.XmlUtil import io.codearte.accurest.dsl.GroovyDsl import nl.flotsam.xeger.Xeger +import java.nio.charset.StandardCharsets + import static org.apache.commons.lang3.StringEscapeUtils.escapeJava class WireMockToDslConverter { @@ -154,11 +156,11 @@ class WireMockToDslConverter { if (!it.name.endsWith('json')) { return } - String dslFromWireMockStub = fromWireMockStub(it.text) + String dslFromWireMockStub = fromWireMockStub(it.getText(StandardCharsets.UTF_8.toString())) String dslWrappedWithFactoryMethod = wrapWithFactoryMethod(dslFromWireMockStub) File newGroovyFile = new File(it.parent, it.name.replaceAll('json', 'groovy')) println("Creating new groovy file [$newGroovyFile.path]") - newGroovyFile.text = dslWrappedWithFactoryMethod + newGroovyFile.setText(dslWrappedWithFactoryMethod, StandardCharsets.UTF_8.toString()) } catch (Exception e) { System.err.println(e) } From dc90d82fd3bb7666c2e2813091529eaf05268c17 Mon Sep 17 00:00:00 2001 From: Mariusz Smykula Date: Thu, 15 Oct 2015 11:00:21 +0200 Subject: [PATCH 116/119] Release version: 0.9.8 [ci skip] From 7a852e1e33b9cc6c79463095958edc8af0ef6a48 Mon Sep 17 00:00:00 2001 From: Adam Wojszczyk Date: Mon, 2 Nov 2015 11:37:14 +0100 Subject: [PATCH 117/119] Add regex to match numbers (integers and decimals) --- .../accurest/dsl/internal/RegexPatterns.groovy | 7 ++++++- .../accurest/dsl/internal/RegexPatternsSpec.groovy | 13 +++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/RegexPatterns.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/RegexPatterns.groovy index 0f530bbf8f..5327bcff66 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/RegexPatterns.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/RegexPatterns.groovy @@ -9,10 +9,11 @@ class RegexPatterns { private static final Pattern TRUE_OR_FALSE = Pattern.compile(/(true|false)/) private static final Pattern ONLY_ALPHA_UNICODE = Pattern.compile(/[\p{L}]*/) + private static final Pattern NUMBER = Pattern.compile('-?\\d*(\\.\\d+)?') private 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])'); private static final Pattern HOSTNAME_PATTERN = Pattern.compile('((http[s]?|ftp):\\/)\\/?([^:\\/\\s]+)(:[0-9]{1,5})?'); private static final Pattern EMAIL = Pattern.compile('[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}'); - private static final Pattern URL = Pattern.compile('((www\\.|(http|https|ftp|news|file)+\\:\\/\\/)[_.a-z0-9-]+\\.[a-z0-9\\/_:@=.+?,##%&~-]*[^.|\\\'|\\# |!|\\(|?|,| |>|<|;|\\)])'); + private static final Pattern URL = Pattern.compile('((www\\.|(http|https|ftp|news|file)+\\:\\/\\/)[_.a-z0-9-]+\\.[a-z0-9\\/_:@=.+?,##%&~-]*[^.|\\\'|\\# |!|\\(|?|,| |>|<|;|\\)])') String onlyAlphaUnicode() { @@ -38,4 +39,8 @@ class RegexPatterns { String url() { return URL.pattern() } + + String number() { + return NUMBER.pattern() + } } diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/internal/RegexPatternsSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/internal/RegexPatternsSpec.groovy index 848ed8511d..bc217f336e 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/internal/RegexPatternsSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/internal/RegexPatternsSpec.groovy @@ -52,4 +52,17 @@ class RegexPatternsSpec extends Specification { 'ftp://asd.com:9090/asd/a?a=b' || true 'a.b.' || false } + + @Unroll + def "should generate a regex for a number [#textToMatch] that is a match [#shouldMatch]"() { + expect: + shouldMatch == Pattern.compile(regexPatterns.number()).matcher(textToMatch).matches() + where: + textToMatch || shouldMatch + '1' || true + '1.0' || true + '0.1' || true + '.1' || true + '1.' || false + } } From 5f42368810d84f87d302282fc23b6313762631d4 Mon Sep 17 00:00:00 2001 From: Adam Wojszczyk Date: Mon, 2 Nov 2015 11:38:58 +0100 Subject: [PATCH 118/119] Add regex to match numbers (integers and decimals) --- .../codearte/accurest/dsl/internal/RegexPatterns.groovy | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/RegexPatterns.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/RegexPatterns.groovy index 5327bcff66..3daa34ec90 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/RegexPatterns.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/RegexPatterns.groovy @@ -20,6 +20,10 @@ class RegexPatterns { return ONLY_ALPHA_UNICODE.pattern() } + String number() { + return NUMBER.pattern() + } + String anyBoolean() { return TRUE_OR_FALSE.pattern() } @@ -39,8 +43,4 @@ class RegexPatterns { String url() { return URL.pattern() } - - String number() { - return NUMBER.pattern() - } } From 943ff5b60813bd2da84f7ae9244dc2094e55cdc5 Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Tue, 3 Nov 2015 13:34:30 +0100 Subject: [PATCH 119/119] Release version: 0.9.9 [ci skip]