diff --git a/.travis.yml b/.travis.yml index 7a0dff7140..bdf2cb280b 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 +script: ./gradlew check funcTest --stacktrace --info --continue diff --git a/README.md b/README.md index 7cead077d6..923a398c25 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,20 @@ Accurate REST ============= [![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 -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) + +## 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 :) 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 62% 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..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,9 +2,10 @@ 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 { +abstract class DslToWireMockConverter implements SingleFileConverter { @Override boolean canHandleFileName(String fileName) { @@ -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") } } 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/RecursiveFilesConverter.groovy b/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/RecursiveFilesConverter.groovy index 5eb8f81d45..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,12 +29,12 @@ 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 convertion of ${sourceFile.name}", 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 similarity index 53% 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 bfc58cc17d..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 @@ -1,52 +1,68 @@ 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 +import nl.flotsam.xeger.Xeger + +import java.nio.charset.StandardCharsets 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 = parseStubDefinition(wireMockStringStub) + Integer priority = wireMockStub.priority + 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(''))" : ""} - ${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('') + ${priority ? "priority ${priority}" : ''} + request { + ${request.method ? "method \"\"\"$request.method\"\"\"" : ""} + ${request.url ? "url \"\"\"$request.url\"\"\"" : ""} + ${urlPattern ? "url \$(client(regex('${escapeJava(urlPattern)}')), server('${new Xeger(escapeJava(urlPattern)).generate()}'))" : ""} + ${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('') + } + } + """ : "" + } + ${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('${new Xeger(escapeJava(bodyPatterns.matches[0])).generate()}'))" : ""} } - } - """ : "" - } - } - response { - ${response.status ? "status $response.status" : ""} - ${response.body ? "body( ${buildBody(response.body)})" : ""} - ${ + 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 Object parseStubDefinition(String wireMockStringStub) { + new JsonSlurper().setType(JsonParserType.LAX).parseText(wireMockStringStub) } private String buildHeader(String method, Object value) { @@ -140,11 +156,11 @@ class WiremockToDslConverter { if (!it.name.endsWith('json')) { return } - String dslFromWiremockStub = fromWiremockStub(it.text) - String dslWrappedWithFactoryMethod = wrapWithFactoryMethod(dslFromWiremockStub) + 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) } @@ -152,10 +168,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/main/groovy/nl/flotsam/xeger/Xeger.java b/accurest-converters/src/main/groovy/nl/flotsam/xeger/Xeger.java new file mode 100644 index 0000000000..9bd42a674a --- /dev/null +++ b/accurest-converters/src/main/groovy/nl/flotsam/xeger/Xeger.java @@ -0,0 +1,118 @@ +/** + * 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. + * + * The class is bundled together with our code because it has not been + * released to any central repository. + * + */ +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/io/codearte/accurest/wiremock/DslToWireMockClientConverterSpec.groovy b/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/DslToWireMockClientConverterSpec.groovy new file mode 100755 index 0000000000..61c6b7c29b --- /dev/null +++ b/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/DslToWireMockClientConverterSpec.groovy @@ -0,0 +1,142 @@ +package io.codearte.accurest.wiremock + +import org.skyscreamer.jsonassert.JSONAssert +import spock.lang.Specification + +class DslToWireMockClientConverterSpec extends Specification { + + def "should convert DSL file to WireMock JSON"() { + given: + 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 + } + } +""" + when: + String json = converter.convertContent(dslBody) + then: + JSONAssert.assertEquals(''' +{"request":{"method":"PUT","urlPattern":"/[0-9]{2}"},"response":{"status":200}} +''', json, false) + } + + + def "should convert DSL file with a nested list to WireMock JSON"() { + given: + def converter = new DslToWireMockClientConverter() + and: + String dslBody = """ + io.codearte.accurest.dsl.GroovyDsl.make { + request { + method 'PUT' + url '/api/12' + headers { + header 'Content-Type': 'application/vnd.com.ofg.twitter-places-analyzer.v1+json' + + } + body ''' + [{ + "created_at": "Sat Jul 26 09:38:57 +0000 2014", + "id": 492967299297845248, + "id_str": "492967299297845248", + "text": "Gonna see you at Warsaw", + "place": + { + "attributes":{}, + "bounding_box": + { + "coordinates": + [[ + [-77.119759,38.791645], + [-76.909393,38.791645], + [-76.909393,38.995548], + [-77.119759,38.995548] + ]], + "type":"Polygon" + }, + "country":"United States", + "country_code":"US", + "full_name":"Washington, DC", + "id":"01fbe706f872cb32", + "name":"Washington", + "place_type":"city", + "url": "http://api.twitter.com/1/geo/id/01fbe706f872cb32.json" + } + }] + ''' + } + response { + status 200 + } + } +""" + when: + String json = converter.convertContent(dslBody) + then: + 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-converters/src/test/groovy/io/codearte/accurest/wiremock/DslToWiremockClientConverterSpec.groovy b/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/DslToWiremockClientConverterSpec.groovy deleted file mode 100755 index 2f20546014..0000000000 --- a/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/DslToWiremockClientConverterSpec.groovy +++ /dev/null @@ -1,101 +0,0 @@ -package io.codearte.accurest.wiremock - -import groovy.json.JsonSlurper -import spock.lang.Specification - -class DslToWiremockClientConverterSpec extends Specification { - - def "should convert DSL file to Wiremock JSON"() { - given: - 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 - } - } -""" - when: - String json = converter.convertContent(dslBody) - then: - new JsonSlurper().parseText(json) == new JsonSlurper().parseText(""" -{"request":{"method":"PUT","urlPattern":"/[0-9]{2}"},"response":{"status":200}}""") - } - - - def "should convert DSL file with a nested list to Wiremock JSON"() { - given: - def converter = new DslToWiremockClientConverter() - and: - String dslBody = """ - io.codearte.accurest.dsl.GroovyDsl.make { - request { - method 'PUT' - url '/api/12' - headers { - header 'Content-Type': 'application/vnd.com.ofg.twitter-places-analyzer.v1+json' - - } - body ''' - [{ - "created_at": "Sat Jul 26 09:38:57 +0000 2014", - "id": 492967299297845248, - "id_str": "492967299297845248", - "text": "Gonna see you at Warsaw", - "place": - { - "attributes":{}, - "bounding_box": - { - "coordinates": - [[ - [-77.119759,38.791645], - [-76.909393,38.791645], - [-76.909393,38.995548], - [-77.119759,38.995548] - ]], - "type":"Polygon" - }, - "country":"United States", - "country_code":"US", - "full_name":"Washington, DC", - "id":"01fbe706f872cb32", - "name":"Washington", - "place_type":"city", - "url": "http://api.twitter.com/1/geo/id/01fbe706f872cb32.json" - } - }] - ''' - } - response { - status 200 - } - } -""" - when: - String json = converter.convertContent(dslBody) - then: - new JsonSlurper().parseText(json) == new JsonSlurper().parseText("""{ - "request":{ - "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\\"}]" } - ], - "headers": { - "Content-Type": { - "equalTo": "application/vnd.com.ofg.twitter-places-analyzer.v1+json" - } - } - }, - "response":{ - "status":200} - } -""") - } -} 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 new file mode 100755 index 0000000000..80e8592276 --- /dev/null +++ b/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/WireMockToDslConverterSpec.groovy @@ -0,0 +1,542 @@ +package io.codearte.accurest.wiremock + +import com.github.tomakehurst.wiremock.stubbing.StubMapping +import io.codearte.accurest.dsl.GroovyDsl +import spock.lang.Specification + +class WireMockToDslConverterSpec extends Specification { + + def 'should produce a Groovy DSL from WireMock stub'() { + 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" + } + } +} +''' + and: + stubMappingIsValidWireMockStub(wireMockStub) + and: + GroovyDsl expectedGroovyDsl = GroovyDsl.make { + request { + method 'GET' + url '/path' + headers { + header('Accept': $( + client(regex('text/.*')), + server('text/plain') + )) + header('X-Custom-Header': $( + client(regex('^.*2134.*$')), + server('121345') + )) + + } + } + response { + status 200 + body( + id: [value: '132'], + surname: 'Kowalsky', + name: 'Jan', + created: '2014-02-02 12:23:43' + ) + headers { + header 'Content-Type': 'text/plain' + + } + } + } + when: + String groovyDsl = WireMockToDslConverter.fromWireMockStub(wireMockStub) + then: + new GroovyShell(this.class.classLoader).evaluate( + """ io.codearte.accurest.dsl.GroovyDsl.make { + $groovyDsl + }""") == expectedGroovyDsl + } + + + def 'should convert WireMock stub with response body containing JSON with escaped double quotes'() { + 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" + } + } +} +''' + and: + stubMappingIsValidWireMockStub(wireMockStub) + and: + GroovyDsl expectedGroovyDsl = GroovyDsl.make { + request { + method 'DELETE' + 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') + } + } + response { + status 200 + body("""{ + "status": "OK" +}""") + headers { + header 'Content-Type': 'application/json' + + } + } + } + when: + String groovyDsl = WireMockToDslConverter.fromWireMockStub(wireMockStub) + then: + new GroovyShell(this.class.classLoader).evaluate( + """ io.codearte.accurest.dsl.GroovyDsl.make { + $groovyDsl + }""") == expectedGroovyDsl + } + + def 'should convert WireMock stub with response body containing integer'() { + given: + String wireMockStub = '''\ +{ + "request": { + "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" + } + } +} +''' + and: + stubMappingIsValidWireMockStub(wireMockStub) + and: + GroovyDsl expectedGroovyDsl = GroovyDsl.make { + request { + method 'POST' + url '/charge/count' + headers { + header('Content-Type': 'application/vnd.creditcard-reporter.v1+json') + } + } + response { + status 200 + body(200) + headers { + header 'Content-Type': 'application/json' + + } + } + } + when: + String groovyDsl = WireMockToDslConverter.fromWireMockStub(wireMockStub) + then: + new GroovyShell(this.class.classLoader).evaluate( + """ io.codearte.accurest.dsl.GroovyDsl.make { + $groovyDsl + }""") == expectedGroovyDsl + } + + def 'should convert WireMock stub with response body as a list'() { + given: + String wireMockStub = '''\ +{ + "request": { + "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" + } + } +} +''' + and: + stubMappingIsValidWireMockStub(wireMockStub) + and: + GroovyDsl expectedGroovyDsl = GroovyDsl.make { + request { + method 'POST' + url '/charge/count' + headers { + header('Content-Type': 'application/vnd.creditcard-reporter.v1+json') + } + } + response { + status 200 + body([ + [a: 1, c: '3'], + 'b', + 'a' + ]) + headers { + header 'Content-Type': 'application/json' + } + } + } + when: + String groovyDsl = WireMockToDslConverter.fromWireMockStub(wireMockStub) + then: + new GroovyShell(this.class.classLoader).evaluate( + """ io.codearte.accurest.dsl.GroovyDsl.make { + $groovyDsl + }""") == expectedGroovyDsl + } + + def 'should convert WireMock stub with response body containing a nested list'() { + given: + String wireMockStub = '''\ +{ + "request": { + "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}]' + } +} +''' + and: + stubMappingIsValidWireMockStub(wireMockStub) + and: + GroovyDsl expectedGroovyDsl = GroovyDsl.make { + request { + method 'POST' + url '/charge/search?pageNumber=0&size=2147483647' + headers { + header('Content-Type': '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 + } +]""") + } + } + when: + String groovyDsl = WireMockToDslConverter.fromWireMockStub(wireMockStub) + then: + new GroovyShell(this.class.classLoader).evaluate( + """ io.codearte.accurest.dsl.GroovyDsl.make { + $groovyDsl + }""") == 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: + stubMappingIsValidWireMockStub(wireMockStub) + 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: + stubMappingIsValidWireMockStub(wireMockStub) + and: + GroovyDsl expectedGroovyDsl = GroovyDsl.make { + request { + method 'POST' + url '/test' + body $(client(~/[0-9]{5}/), server('12345')) + } + 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 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('12')) + } + 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 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-converters/src/test/groovy/io/codearte/accurest/wiremock/WiremockToDslConverterSpec.groovy b/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/WiremockToDslConverterSpec.groovy deleted file mode 100755 index 7466b2e7f5..0000000000 --- a/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/WiremockToDslConverterSpec.groovy +++ /dev/null @@ -1,290 +0,0 @@ -package io.codearte.accurest.wiremock - -import io.codearte.accurest.dsl.GroovyDsl -import spock.lang.Specification - -class WiremockToDslConverterSpec extends Specification { - - def 'should produce a Groovy DSL from Wiremock stub'() { - 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", - } - } -} -''' - and: - GroovyDsl expectedGroovyDsl = GroovyDsl.make { - request { - method 'GET' - url '/path' - headers { - header('Accept': $( - client(regex('text/.*')), - server('text/plain') - )) - header('X-Custom-Header': $( - client(regex('^.*2134.*$')), - server('121345') - )) - - } - } - response { - status 200 - body( - id: [value: '132'], - surname: 'Kowalsky', - name: 'Jan', - created: '2014-02-02 12:23:43' - ) - headers { - header 'Content-Type': 'text/plain' - - } - } - } - when: - String groovyDsl = WiremockToDslConverter.fromWiremockStub(wiremockStub) - then: - new GroovyShell(this.class.classLoader).evaluate( - """ io.codearte.accurest.dsl.GroovyDsl.make { - $groovyDsl - }""") == expectedGroovyDsl - } - - - def 'should convert Wiremock stub with body containing simple JSON'() { - 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" - } - } -} -''' - and: - GroovyDsl expectedGroovyDsl = GroovyDsl.make { - request { - method 'DELETE' - url $(client(~/\/credit-card-verification-data\/[0-9]+/), server('')) - headers { - header('Content-Type': 'application/vnd.mymoid-adapter.v2+json; charset=UTF-8') - } - } - response { - status 200 - body("""{ - "status": "OK" -}""") - headers { - header 'Content-Type': 'application/json' - - } - } - } - when: - String groovyDsl = WiremockToDslConverter.fromWiremockStub(wiremockStub) - then: - new GroovyShell(this.class.classLoader).evaluate( - """ io.codearte.accurest.dsl.GroovyDsl.make { - $groovyDsl - }""") == expectedGroovyDsl - } - - def 'should convert Wiremock stub with body containing integer'() { - given: - String wiremockStub = '''\ -{ - "request": { - "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" - } - } -} -''' - and: - GroovyDsl expectedGroovyDsl = GroovyDsl.make { - request { - method 'POST' - url '/charge/count' - headers { - header('Content-Type': 'application/vnd.creditcard-reporter.v1+json') - } - } - response { - status 200 - body(200) - headers { - header 'Content-Type': 'application/json' - - } - } - } - when: - String groovyDsl = WiremockToDslConverter.fromWiremockStub(wiremockStub) - then: - new GroovyShell(this.class.classLoader).evaluate( - """ io.codearte.accurest.dsl.GroovyDsl.make { - $groovyDsl - }""") == expectedGroovyDsl - } - - def 'should convert Wiremock stub with body as a list'() { - given: - String wiremockStub = '''\ -{ - "request": { - "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" - } - } -} -''' - and: - GroovyDsl expectedGroovyDsl = GroovyDsl.make { - request { - method 'POST' - url '/charge/count' - headers { - header('Content-Type': 'application/vnd.creditcard-reporter.v1+json') - } - } - response { - status 200 - body([ - [a: 1, c: '3'], - 'b', - 'a' - ]) - headers { - header 'Content-Type': 'application/json' - } - } - } - when: - String groovyDsl = WiremockToDslConverter.fromWiremockStub(wiremockStub) - then: - new GroovyShell(this.class.classLoader).evaluate( - """ io.codearte.accurest.dsl.GroovyDsl.make { - $groovyDsl - }""") == expectedGroovyDsl - } - - - def 'should convert Wiremock stub with body containing a nested list'() { - given: - String wiremockStub = '''\ -{ - "request": { - "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}]" - } -} -''' - and: - GroovyDsl expectedGroovyDsl = GroovyDsl.make { - request { - method 'POST' - url '/charge/search?pageNumber=0&size=2147483647' - headers { - header('Content-Type': '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 - } -]""") - } - } - when: - String groovyDsl = WiremockToDslConverter.fromWiremockStub(wiremockStub) - then: - new GroovyShell(this.class.classLoader).evaluate( - """ io.codearte.accurest.dsl.GroovyDsl.make { - $groovyDsl - }""") == expectedGroovyDsl - } - -} 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/accurest-core/src/main/groovy/io/codearte/accurest/SingleTestGenerator.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/SingleTestGenerator.groovy index bc6f35adde..fb2d135419 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.*') @@ -47,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.targetFramework)) + 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/TestGenerator.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/TestGenerator.groovy index a502448bc4..719b5cadca 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 @@ -66,19 +67,19 @@ class TestGenerator { if (filesToClass.size()) { def className = afterLast(includedDirectoryRelativePath, File.separator) + configProperties.targetFramework.classNameSuffix def packageName = buildPackage(packageNameForClass, includedDirectoryRelativePath) - def classBytes = generator.buildClass(filesToClass, className, packageName).bytes + def classBytes = generator.buildClass(filesToClass, className, packageName).getBytes(StandardCharsets.UTF_8) saver.saveClassFile(className, packageName, classBytes) counter.incrementAndGet() } } } - private static String buildPackage(final String packageNameForClass, final String includedDirectoryRelativePath) { - String directory = beforeLast(includedDirectoryRelativePath, File.separator) - return "$packageNameForClass.${directoryToPackage(directory)}" - } + private static String buildPackage(final String packageNameForClass, final String includedDirectoryRelativePath) { + String directory = beforeLast(includedDirectoryRelativePath, File.separator) + return "$packageNameForClass.${directoryToPackage(directory)}" + } - private static String normalizePath(String path) { - return FilenameUtils.separatorsToUnix(path) - } + private static 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/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/JaxRsClientSpockMethodBodyBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodBodyBuilder.groovy new file mode 100644 index 0000000000..91ba58e4f0 --- /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..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 @@ -1,9 +1,12 @@ 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 +import org.codehaus.groovy.control.CompilerConfiguration /** * @author Jakub Kubrynski @@ -13,28 +16,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) + 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, 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..af84103be2 --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBodyBuilder.groovy @@ -0,0 +1,90 @@ +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 + +import java.util.regex.Pattern + +@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('${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() + + 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') ${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()' + } + + 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 = "" + 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" + } + +} 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..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,104 +1,216 @@ 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 -import io.codearte.accurest.dsl.internal.ExecutionProperty -import io.codearte.accurest.dsl.internal.Header - -import java.util.regex.Pattern +import io.codearte.accurest.dsl.internal.* +import io.codearte.accurest.util.ContentType +import io.codearte.accurest.util.MapConverter +import io.codearte.accurest.util.JsonToJsonPathsConverter +import io.codearte.accurest.util.JsonPaths +import static io.codearte.accurest.util.ContentUtils.* /** * @author Jakub Kubrynski */ @PackageScope -class SpockMethodBodyBuilder { - private final GroovyDsl stubDefinition +@TypeChecked +abstract class SpockMethodBodyBuilder { + + private static final Boolean TEST_SIDE = false + + 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) { 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, "") - } - } - blockBuilder.endBlock() + givenBlock(blockBuilder) + whenBlock(blockBuilder) + thenBlock(blockBuilder) blockBuilder.endBlock() } - private void processBodyElement(BlockBuilder blockBuilder, String rootProperty, def element) { - def value = element.value - String property = rootProperty + "." + element.key - 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 List) { - processArrayElements(value, property, blockBuilder) - } else if (value instanceof Pattern) { - blockBuilder.addLine("responseBody$property ==~ java.util.regex.Pattern.compile('${value}')") - } else if (value instanceof ExecutionProperty) { - ExecutionProperty exec = (ExecutionProperty) value - blockBuilder.addLine("${exec.insertValue("responseBody$property")}") - } else { - blockBuilder.addLine("responseBody$property == ${value}") + protected void thenBlock(BlockBuilder bb) { + bb.addLine('then:') + bb.startBlock() + then(bb) + bb.endBlock() + } + + protected void whenBlock(BlockBuilder bb) { + bb.addLine('when:') + bb.startBlock() + when(bb) + bb.endBlock().addEmptyLine() + } + + protected void givenBlock(BlockBuilder bb) { + bb.addLine('given:') + bb.startBlock() + given(bb) + bb.endBlock().addEmptyLine() + } + + protected void given(BlockBuilder bb) {} + + 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 void processMapElement(def value, BlockBuilder blockBuilder, String property) { - value.each { entry -> processBodyElement(blockBuilder, property, entry) } - } - - private void processArrayElements(List responseBody, String property, BlockBuilder blockBuilder) { - responseBody.eachWithIndex { - listElement, listIndex -> - listElement.each { - entry -> processBodyElement(blockBuilder, property + "[$listIndex]", entry) + 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) { + appendJsonPath(bb, responseAsString) + JsonPaths jsonPaths = JsonToJsonPathsConverter.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)") + processText(bb, "", responseBody as String) } } - private void processClosure(Closure value, BlockBuilder blockBuilder, String property) { - blockBuilder.addLine() + + 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) + 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) { + return toTrim.startsWith('"') ? toTrim.replaceAll('"', '') : toTrim + } + + protected Object extractServerValueFromBody(bodyValue) { + if (bodyValue instanceof GString) { + bodyValue = extractValue(bodyValue, { DslProperty dslProperty -> dslProperty.serverValue }) + } else { + bodyValue = MapConverter.transformValues(bodyValue, { it instanceof DslProperty ? it.serverValue : it }) + } + return bodyValue + } + + protected boolean allowedQueryParameter(QueryParameter param) { + return allowedQueryParameter(param.serverValue) + } + + protected boolean allowedQueryParameter(MatchingStrategy matchingStrategy) { + return matchingStrategy.type != MatchingStrategy.Type.ABSENT + } + + protected boolean allowedQueryParameter(Object o) { + return true + } + + protected String resolveParamValue(QueryParameter param) { + return resolveParamValue(param.serverValue) + } + + protected String resolveParamValue(Object value) { + return value.toString() + } + + protected String resolveParamValue(MatchingStrategy matchingStrategy) { + return matchingStrategy.serverValue.toString() + } + + protected void processBodyElement(BlockBuilder blockBuilder, String property, Object value) { + + } + + 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("parsedJson.read('\\\$$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 + } + + protected String getTestSideValue(Object object) { + return MapConverter.getClientOrServerSideValues(object, TEST_SIDE).toString() } } 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/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/dsl/BaseWireMockStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/BaseWireMockStubStrategy.groovy new file mode 100755 index 0000000000..c485285281 --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/BaseWireMockStubStrategy.groovy @@ -0,0 +1,103 @@ +package io.codearte.accurest.dsl + +import groovy.json.JsonBuilder +import groovy.transform.TypeChecked +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 + +import static io.codearte.accurest.util.ContentUtils.extractValue +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 + } + + protected Map buildClientRequestHeadersSection(Headers headers) { + if (!headers) { + return null + } + return headers.entries.collectEntries { Header entry -> + parseHeader(entry.name, entry.clientValue) + } + } + + protected Map buildClientResponseHeadersSection(Headers headers) { + if (!headers) { + return null + } + return headers.entries.collectEntries { Header entry -> + [(entry.name): entry.clientValue] + } + } + + protected Map parseHeader(String entryKey, Object entry) { + return [(entryKey): [equalTo: entry]] + } + + protected Map parseHeader(String entryKey, String entry) { + return [(entryKey): [equalTo: entry]] + } + + protected Map parseHeader(String entryKey, Pattern entry) { + return [(entryKey): [matches: entry.pattern()]] + } + + public String parseBody(Object value, ContentType contentType) { + return parseBody(value.toString(), contentType) + } + + public Boolean parseBody(Boolean value, ContentType contentType) { + return value + } + + public String parseBody(Map map, ContentType contentType) { + def transformedMap = MapConverter.getClientOrServerSideValues(map, true) + return parseBody(toJson(transformedMap), contentType) + } + + 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() + } + + 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/BaseWiremockStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/BaseWiremockStubStrategy.groovy deleted file mode 100755 index 77866f1437..0000000000 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/BaseWiremockStubStrategy.groovy +++ /dev/null @@ -1,67 +0,0 @@ -package io.codearte.accurest.dsl -import groovy.json.JsonOutput -import groovy.json.JsonSlurper -import groovy.transform.TypeChecked -import groovy.xml.XmlUtil -import io.codearte.accurest.dsl.internal.Header -import io.codearte.accurest.dsl.internal.Headers - -import java.util.regex.Pattern - -import static groovy.json.StringEscapeUtils.escapeJava - -@TypeChecked -abstract class BaseWiremockStubStrategy { - protected Map buildClientRequestHeadersSection(Headers headers) { - if (!headers) { - return null - } - return headers.entries.collectEntries { Header entry -> - parseHeader(entry.name, entry.clientValue) - } - } - - protected Map buildClientResponseHeadersSection(Headers headers) { - if (!headers) { - return null - } - return headers.entries.collectEntries { Header entry -> - [(entry.name) : entry.clientValue] - } - } - - protected Map parseHeader(String entryKey, Object entry) { - return [(entryKey): [equalTo : entry]] - } - - protected Map parseHeader(String entryKey, String entry) { - return [(entryKey): [equalTo : entry]] - } - - protected Map parseHeader(String entryKey, Pattern entry) { - 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) - } - } - } - - protected String parseBody(List body) { - return JsonOutput.toJson(body) - } - - protected String parseBody(Map body) { - return JsonOutput.toJson(body) - } -} 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..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 @@ -7,10 +7,11 @@ 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 { + Integer priority Request request Response response @@ -21,6 +22,10 @@ class GroovyDsl { return dsl } + void priority(int priority) { + this.priority = 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/WireMockRequestStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockRequestStubStrategy.groovy new file mode 100755 index 0000000000..cf69737c95 --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockRequestStubStrategy.groovy @@ -0,0 +1,232 @@ +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 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.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.buildGStringRegexpForStubSide +import static io.codearte.accurest.util.RegexpBuilders.buildJSONRegexpMatch + +@TypeChecked +@PackageScope +class WireMockRequestStubStrategy extends BaseWireMockStubStrategy { + + private final Request request + + WireMockRequestStubStrategy(GroovyDsl groovyDsl) { + this.request = groovyDsl.request + } + + @PackageScope + RequestPattern buildClientRequestContent() { + RequestPattern requestPattern = new RequestPattern() + appendMethod(requestPattern) + appendHeaders(requestPattern) + appendUrl(requestPattern) + appendQueryParameters(requestPattern) + appendBody(requestPattern) + return requestPattern + } + + private void appendMethod(RequestPattern requestPattern) { + if(!request.method) { + return + } + requestPattern.setMethod(RequestMethod.fromString(request.method.clientValue?.toString())) + } + + private void appendBody(RequestPattern requestPattern) { + if (!request.body) { + return + } + ContentType contentType = tryToGetContentType(request.body.clientValue, request.headers) + if (contentType == ContentType.JSON) { + 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) ) ] + } 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 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) { + requestPattern.setUrlPath(getStubSideValue(urlPath.toString()).toString()) + } + if(!request.url) { + return + } + Object url = getUrlIfGstring(request?.url?.clientValue) + if(url instanceof Pattern) { + requestPattern.setUrlPattern(url.pattern()) + } else { + requestPattern.setUrl(url.toString()) + } + } + + 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 { + requestPattern.addQueryParam(it.name, convertToValuePattern(it.clientValue)) + } + } + + @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()) + } + } + + private MatchingStrategy getMatchingStrategyFromBody(Body body) { + if(!body) { + return null + } + return getMatchingStrategy(body.clientValue) + } + + 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 def getStringFromGString(Object object) { + return object instanceof GString ? object.toString() : object + } + + private MatchingStrategy getMatchingStrategy(Object bodyValue) { + return tryToFindMachingStrategy(bodyValue) + } + + private MatchingStrategy tryToFindMachingStrategy(Object bodyValue) { + return new MatchingStrategy(MapConverter.transformToClientValues(bodyValue), getEqualsTypeFromContentTypeHeader()) + } + + private MatchingStrategy getMatchingStrategyIncludingContentType(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 new MatchingStrategy(parseBody(value, contentType), type) + } + + private MatchingStrategy appendBodyRegexpMatchPattern(Object value, ContentType contentType) { + switch (contentType) { + case ContentType.JSON: + return new MatchingStrategy(buildJSONRegexpMatch(value), MatchingStrategy.Type.MATCHING) + case ContentType.UNKNOWN: + return new MatchingStrategy(buildGStringRegexpForStubSide(value), MatchingStrategy.Type.MATCHING) + case ContentType.XML: + throw new IllegalStateException("XML pattern matching is not implemented yet") + } + } + + private MatchingStrategy appendBodyRegexpMatchPattern(Object value) { + return appendBodyRegexpMatchPattern(value, ContentType.UNKNOWN) + } + + private boolean containsPattern(GString bodyAsValue) { + return containsPattern(bodyAsValue.values) + } + + 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() { + 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 new file mode 100755 index 0000000000..3f63e6433c --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockResponseStubStrategy.groovy @@ -0,0 +1,57 @@ +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 +import groovy.transform.PackageScope +import groovy.transform.TypeChecked +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 + ResponseDefinition buildClientResponseContent() { + ResponseDefinitionBuilder builder = new ResponseDefinitionBuilder() + .withStatus(response.status.clientValue as Integer) + appendHeaders(builder) + appendBody(builder) + return builder.build() + } + + private void appendHeaders(ResponseDefinitionBuilder builder) { + if (response.headers) { + builder.withHeaders(new HttpHeaders(response.headers.entries?.collect { + new HttpHeader(it.name, it.clientValue.toString()) + })) + } + } + + 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)) + } + } + + +} 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..337f7b8557 --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockStubStrategy.groovy @@ -0,0 +1,34 @@ +package io.codearte.accurest.dsl + +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 + +@CompileStatic +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() { + StubMapping stubMapping = new StubMapping() + RequestPattern request = wireMockRequestStubStrategy.buildClientRequestContent() + ResponseDefinition response = wireMockResponseStubStrategy.buildClientResponseContent() + if (priority) { + stubMapping.priority = priority + } + 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/WiremockRequestStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy deleted file mode 100755 index 36f7ec1ab5..0000000000 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockRequestStubStrategy.groovy +++ /dev/null @@ -1,52 +0,0 @@ -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.Request - -import java.util.regex.Pattern - -@TypeChecked -@PackageScope -class WiremockRequestStubStrategy extends BaseWiremockStubStrategy { - - private final Request request - - WiremockRequestStubStrategy(GroovyDsl groovyDsl) { - this.request = groovyDsl.request - } - - @PackageScope - Map buildClientRequestContent() { - return buildRequestContent(new ClientRequest(request)) - } - - private Map buildRequestContent(ClientRequest request) { - return ([method : request?.method?.clientValue, - headers : buildClientRequestHeadersSection(request.headers) - ] << appendUrl(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] - } - - private Map appendBody(ClientRequest clientRequest) { - Object body = clientRequest?.body?.clientValue - if (body == null) { - return [:] - } - if (containsRegex(body)) { - return [bodyPatterns: [[matches: parseBody(body)]]] - } - - return [bodyPatterns: [[equalTo: parseBody(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/WiremockResponseStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockResponseStubStrategy.groovy deleted file mode 100755 index e0b7556547..0000000000 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WiremockResponseStubStrategy.groovy +++ /dev/null @@ -1,32 +0,0 @@ -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.Response - -@TypeChecked -@PackageScope -class WiremockResponseStubStrategy extends BaseWiremockStubStrategy { - - private final Response response - - WiremockResponseStubStrategy(GroovyDsl groovyDsl) { - this.response = groovyDsl.response - } - - @PackageScope - Map buildClientResponseContent() { - return buildResponseContent(new ClientResponse(response)) - } - - private Map buildResponseContent(ClientResponse response) { - return ([status : response?.status?.clientValue, - 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)] : [:] - } -} 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/main/groovy/io/codearte/accurest/dsl/internal/Body.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Body.groovy index 5e8d46ed98..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,16 +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 org.codehaus.groovy.runtime.GStringImpl @ToString(includePackage = false, includeFields = true, includeNames = true) @EqualsAndHashCode(includeFields = true) +@CompileStatic class Body extends DslProperty { 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) { @@ -19,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) { @@ -28,17 +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) } - 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()) + Body(MatchingStrategy matchingStrategy) { + super(matchingStrategy, matchingStrategy) } - + + } 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..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 @@ -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 -> @@ -20,10 +22,10 @@ class Common { } as Map } - List convertObjectsToDslProperties(List body) { - return body.collect { + Collection convertObjectsToDslProperties(List body) { + return (body.collect { Object element -> toDslProperty(element) - } as List + } as List) } DslProperty toDslProperty(Object property) { @@ -47,10 +49,16 @@ class Common { } DslProperty value(ClientDslProperty client, ServerDslProperty server) { + assertThatSidesMatch(client.clientValue, server.serverValue) 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) } @@ -66,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) } @@ -74,7 +86,53 @@ 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(OptionalProperty stubSide, Object testSide) { + assert testSide ==~ Pattern.compile(stubSide.optionalPattern()) + } + + void assertThatSidesMatch(Pattern pattern, String value) { + assert value ==~ pattern + } + + void assertThatSidesMatch(String value, Pattern pattern) { + assert value ==~ pattern + } + + 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/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/JsonStructureConverter.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/JsonStructureConverter.groovy new file mode 100644 index 0000000000..b6b6f79eee --- /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.MapConverter + +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 = MapConverter.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/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..191af8d7ee --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/MatchingStrategy.groovy @@ -0,0 +1,47 @@ +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 +class MatchingStrategy extends DslProperty { + + Type type + JSONCompareMode jsonCompareMode + + MatchingStrategy(Object value, Type type) { + this(value, type, null) + } + + MatchingStrategy(Object value, Type type, JSONCompareMode jsonCompareMode) { + super(value) + this.type = type + this.jsonCompareMode = jsonCompareMode + } + + MatchingStrategy(DslProperty value, Type type) { + this(value, type, null) + } + + MatchingStrategy(DslProperty value, Type type, JSONCompareMode jsonCompareMode) { + super(value.clientValue, value.serverValue) + this.type = type + this.jsonCompareMode = jsonCompareMode + } + + enum Type { + + EQUAL_TO("equalTo"), CONTAINS("containing"), MATCHING("matches"), NOT_MATCHING("doesNotMatch"), + EQUAL_TO_JSON("equalToJson"), EQUAL_TO_XML("equalToXml"), ABSENT("absent") + + 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/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/QueryParameter.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameter.groovy new file mode 100644 index 0000000000..153fa6f297 --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameter.groovy @@ -0,0 +1,34 @@ +package io.codearte.accurest.dsl.internal; + +import groovy.transform.CompileStatic; +import groovy.transform.EqualsAndHashCode; +import groovy.transform.ToString + +import static io.codearte.accurest.util.ValidateUtils.validateServerValueIsAvailable; + +@EqualsAndHashCode(includeFields = true) +@ToString(includePackage = false, includeFields = true, ignoreNulls = true, includeNames = true) +@CompileStatic +class QueryParameter extends DslProperty { + + String 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, 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 new file mode 100644 index 0000000000..b4cc1d74c4 --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/QueryParameters.groovy @@ -0,0 +1,23 @@ +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 +class QueryParameters { + + List parameters = [] + + 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) + } + +} 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..3daa34ec90 --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/RegexPatterns.groovy @@ -0,0 +1,46 @@ +package io.codearte.accurest.dsl.internal + +import groovy.transform.CompileStatic + +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 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\\/_:@=.+?,##%&~-]*[^.|\\\'|\\# |!|\\(|?|,| |>|<|;|\\)])') + + + String onlyAlphaUnicode() { + return ONLY_ALPHA_UNICODE.pattern() + } + + String number() { + return NUMBER.pattern() + } + + String anyBoolean() { + return TRUE_OR_FALSE.pattern() + } + + 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/main/groovy/io/codearte/accurest/dsl/internal/Request.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Request.groovy index 77652a9396..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 @@ -5,12 +5,13 @@ import groovy.transform.ToString import groovy.transform.TypeChecked @TypeChecked -@EqualsAndHashCode(includeFields = true) +@EqualsAndHashCode @ToString(includePackage = false, includeNames = true) 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 @@ -54,6 +88,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) } @@ -61,10 +99,43 @@ 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 absent() { + 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 -@EqualsAndHashCode(includeFields = true) +@EqualsAndHashCode @ToString(includePackage = false) class ServerRequest extends Request { ServerRequest(Request request) { @@ -73,7 +144,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..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 @@ -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,13 @@ class Response extends Common { this.body = new Body(bodyAsValue) } - Body getBody() { - return body - } - - DslProperty getStatus() { - return status - } - - Headers getHeaders() { - return headers + void assertThatSidesMatch(OptionalProperty stubSide, Object testSide) { + throw new IllegalStateException("Optional can be used only in the test side of the response!") } } @CompileStatic -@EqualsAndHashCode(includeFields = true) +@EqualsAndHashCode @ToString(includePackage = false) class ServerResponse extends Response { ServerResponse(Response request) { @@ -72,7 +64,7 @@ class ServerResponse extends Response { } @CompileStatic -@EqualsAndHashCode(includeFields = true) +@EqualsAndHashCode @ToString(includePackage = false) class ClientResponse extends Response { ClientResponse(Response request) { 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..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 @@ -1,18 +1,32 @@ package io.codearte.accurest.dsl.internal +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 class Url extends DslProperty { - Url(DslProperty bodyAsValue) { - super(bodyAsValue.clientValue, bodyAsValue.serverValue) + QueryParameters queryParameters + + Url(DslProperty prop) { + super(prop.clientValue, prop.serverValue) + validateServerValueIsAvailable(prop.serverValue, "Url") } - Url(String bodyAsValue) { - super(bodyAsValue) + Url(Object url) { + super(url) + validateServerValueIsAvailable(url, "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..68d9bc0f7d --- /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/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..a9243058af --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentType.groovy @@ -0,0 +1,15 @@ +package io.codearte.accurest.util + +enum ContentType { + + 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/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..1b370b7126 --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy @@ -0,0 +1,323 @@ +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 +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.OptionalProperty +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 +@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 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_PATTERN_FOR_OPTIONAL = 'OPTIONAL>>%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 + * 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 (bodyAsValue.isEmpty()){ + return bodyAsValue + } + if (contentType == ContentType.JSON) { + return extractValueForJSON(bodyAsValue, valueProvider) + } + if (contentType == ContentType.XML) { + return extractValueForXML(bodyAsValue, valueProvider) + } + // 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 + 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 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) { + 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) + } + + 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().replace('\\', '\\\\')) + 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 transformJSONStringValue(OptionalProperty optional, Closure valueProvider) { + return String.format(JSON_VALUE_PATTERN_FOR_OPTIONAL, optional.value) + } + + 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()) + } + + private static String transformXMLStringValue(DslProperty dslProperty, Closure valueProvider) { + return transformXMLStringValue(valueProvider(dslProperty), valueProvider) + } + + private static Object convertAllTemporaryRegexPlaceholdersBackToPatterns(parsedJson) { + MapConverter.transformValues(parsedJson, { Object value -> + if (value instanceof String) { + String string = (String) value + return returnParsedObject(string) + } + return value + }) + } + + /** + *

+ * 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()) { + return Pattern.compile(patternFromMatchingGroup(matcher)) + } + Matcher executionMatcher = TEMPORARY_EXECUTION_PATTERN_HOLDER.matcher(string.trim()) + if (executionMatcher.matches()) { + return new ExecutionProperty(patternFromMatchingGroup(executionMatcher)) + } + Matcher optionalMatcher = TEMPORARY_OPTIONAL_PATTERN_HOLDER.matcher(string.trim()) + if (optionalMatcher.matches()) { + 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")) { + 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) { + if (gstring.isEmpty()) { + return false + } + 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/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..be1638027e --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonPathEntry.groovy @@ -0,0 +1,40 @@ +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).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/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/JsonToJsonPathsConverter.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonToJsonPathsConverter.groovy new file mode 100644 index 0000000000..319e7ab660 --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/JsonToJsonPathsConverter.groovy @@ -0,0 +1,148 @@ +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 + +/** + * @author Marcin Grzejszczak + */ +class JsonToJsonPathsConverter { + + 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 = MapConverter.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 + } + + 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 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 new file mode 100644 index 0000000000..7087f9c1d7 --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/MapConverter.groovy @@ -0,0 +1,76 @@ +package io.codearte.accurest.util +import groovy.json.JsonSlurper +import io.codearte.accurest.dsl.internal.DslProperty +/** + * @author Marcin Grzejszczak + */ +class MapConverter { + + static def transformToClientValues(def value) { + return transformValues(value) { + it instanceof DslProperty ? it.clientValue : it + } + } + + 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 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) { + return value + } + } + + private static Map convert(Map map, Closure closure) { + return map.collectEntries { + key, value -> + [key, transformValues(value, closure)] + } + } + + 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/main/groovy/io/codearte/accurest/util/NamesUtil.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/NamesUtil.groovy index 88172ca90b..5b19c72645 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 @@ -47,7 +47,7 @@ class NamesUtil { } static String packageToDirectory(String packageName) { - return packageName.replaceAll('\\.', File.separator) + return packageName.replace('.' as char, File.separatorChar) } static String directoryToPackage(String directory) { 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..6c8b703fca --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/RegexpBuilders.groovy @@ -0,0 +1,91 @@ +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 buildGStringRegexpForStubSide(GString gString) { + new GStringImpl( + gString.values.collect(this.&buildGStringRegexpForStubSide) as Object[], + gString.strings.collect(this.&escapeSpecialRegexChars) as String[] + ) + } + + public static String buildGStringRegexpForStubSide(Pattern pattern) { + return pattern.pattern() + } + + public static String buildGStringRegexpForStubSide(DslProperty dslProperty) { + return buildGStringRegexpForStubSide(dslProperty.clientValue) + } + + 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') + } + + 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/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/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..46e9706cfe --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/ValidateUtils.groovy @@ -0,0 +1,46 @@ +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 + +import static io.codearte.accurest.dsl.internal.MatchingStrategy.Type.ABSENT +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, 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 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(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/builder/JaxRsClientSpockMethodBuilderSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodBuilderSpec.groovy new file mode 100644 index 0000000000..aa5a26cb5d --- /dev/null +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodBuilderSpec.groovy @@ -0,0 +1,414 @@ +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 implements WireMockStubVerifier { + + 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("\$[?(@.property1 == 'a')]") + blockBuilder.toString().contains("\$[?(@.property2 == 'b')]") + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) + } + + @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("\$[?(@.property1 == 'a')]") + blockBuilder.toString().contains("\$.property2[*][?(@.a == 'sth')]") + blockBuilder.toString().contains("\$.property2[*][?(@.b == 'sthElse')]") + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) + } + + @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')") + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) + } + + @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')") + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) + } + + 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("\$[*][?(@.property1 == 'a')]") + blockBuilder.toString().contains("\$[*][?(@.property2 == 'b')]") + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) + } + + 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("\$.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"() { + 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("\$.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"() { + 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("\$[?(@.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"() { + 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("\$[?(@.property2 =~ /[0-9]{3}/)]") + blockBuilder.toString().contains("\$[?(@.property1 == 'a')]") + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) + } + + 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')") + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) + } + + 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'") + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) + } + + 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('$[?(@.property2 == \'b\')]') + spockTest.contains('$[?(@.property1 == \'a\')]') + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) + } + + 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')") + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) + } + + 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"') + 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 new file mode 100644 index 0000000000..5e8139172f --- /dev/null +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBuilderSpec.groovy @@ -0,0 +1,773 @@ +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 +import spock.lang.Unroll + +import java.util.regex.Pattern + +/** + * @author Jakub Kubrynski + */ +class MockMvcSpockMethodBuilderSpec extends Specification implements WireMockStubVerifier { + + 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" +}""" + } + } + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains("\$[?(@.property1 == 'a')]") + blockBuilder.toString().contains("\$[?(@.property2 == 'b')]") + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) + } + + @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'] + ] + ) + } + } + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + 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") + 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 + } + } + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains(".body('{\"items\":[\"HOP\"]}')") + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) + } + + @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 + } + } + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + 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"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "GET" + url "test" + } + response { + status 200 + body """[ +{ + "property1": "a" +}, +{ + "property2": "b" +}]""" + } + } + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + 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"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "GET" + url "test" + } + response { + status 200 + body """{ + "property1": [ + { "property2": "test1"}, + { "property3": "test2"} + ] +}""" + } + } + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + 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"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method "GET" + url "test" + } + response { + status 200 + body '''\ +{ + "property1": "a", + "property2": {"property3": "b"} +} +''' + } + } + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + 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"() { + 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()) + } + + 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 { + method "GET" + url "test" + } + response { + status 200 + body("""{"property":" ${value(client('123'), server(regex('\\d+')))}"}""") + headers { + header('Content-Type': 'application/json') + } + } + } + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + then: + blockBuilder.toString().contains("\$[?(@.property =~ /\\d+/)]") + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) + } + + 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" + } + """ + } + } + 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('$[?(@.property2 == \'b\')]') + spockTest.contains('$[?(@.property1 == \'a\')]') + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) + } + + def "should generate test for empty body"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + request { + method('POST') + url("/ws/payments") + body("") + } + response { + status 406 + } + } + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + def spockTest = blockBuilder.toString() + then: + spockTest.contains(".body('')") + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) + } + + 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"') + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) + } + + @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]+')''') + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) + } + + @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]+')''') + and: + stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub()) + } + + def "should work with more complex stuff and jsonpaths"() { + given: + GroovyDsl contractDsl = GroovyDsl.make { + priority 10 + request { + method 'POST' + 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 200 + body(errors: [ + [property: "bank_account_number", message: "incorrect_format"] + ]) + } + } + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + def spockTest = blockBuilder.toString() + 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"() { + 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}\\\\]/)]''') + } + + @Issue('42') + @Unroll + def "should not omit the optional field in the test creation"() { + given: + MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + def spockTest = blockBuilder.toString() + then: + spockTest.contains('''"email":"abc@abc.com"''') + spockTest.contains('''parsedJson.read(\'\'\'$[?(@.code =~ /(123123)?/)]''') + !spockTest.contains('''REGEXP''') + !spockTest.contains('''OPTIONAL''') + !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')) + ) + } + 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(stub(123123), test(optional(123123)))}", + "message" : "User not found by email = [${value(server(regex(email())), client('not.existing@user.com'))}]" + } + """ + ) + } + } + ] + } + + @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'))''') + } + + 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") + } + + + 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") + } + +} 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 deleted file mode 100644 index c68a96a1e6..0000000000 --- a/accurest-core/src/test/groovy/io/codearte/accurest/builder/SpockMethodBuilderSpec.groovy +++ /dev/null @@ -1,113 +0,0 @@ -package io.codearte.accurest.builder - -import io.codearte.accurest.dsl.GroovyDsl -import spock.lang.Specification - -/** - * @author Jakub Kubrynski - */ -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 """{ - "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\"") - } - - 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\"") - } - - 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\"") - } - - 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\"") - } -} 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 new file mode 100755 index 0000000000..7502ada771 --- /dev/null +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy @@ -0,0 +1,1401 @@ +package io.codearte.accurest.dsl + +import groovy.json.JsonBuilder +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 { + + def 'should convert groovy dsl stub to wireMock stub for the client side'() { + given: + GroovyDsl groovyDsl = GroovyDsl.make { + request { + method('GET') + url $(client(~/\/[0-9]{2}/), server('/12')) + } + response { + status 200 + 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) })) + ) + headers { + header 'Content-Type': 'text/plain' + } + } + } + when: + String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() + then: + AssertionUtil.assertThatJsonsAreEqual(''' + { + "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) + and: + 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: + AssertionUtil.assertThatJsonsAreEqual(''' +{ + "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) + and: + 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: + 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}" + } +} +''', wireMockStub) + 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 { + request { + method('GET') + url $(client(~/\/[0-9]{2}/), server('/12')) + } + 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'))}" + } + """ + ) + headers { + header 'Content-Type': 'text/plain' + } + } + } + when: + String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() + then: + AssertionUtil.assertThatJsonsAreEqual((''' +{ + "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) + and: + stubMappingIsValidWireMockStub(wireMockStub) + } + + def 'should convert groovy dsl stub with simple 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 """ + { + "name": "Jan" + } + """ + } + response { + status 200 + body("""\ + { + "name": "Jan" + } + """ + ) + headers { + header 'Content-Type': 'text/plain' + } + } + } + when: + String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() + then: + AssertionUtil.assertThatJsonsAreEqual(''' +{ + "request" : { + "urlPattern" : "/[0-9]{2}", + "method" : "GET", + "bodyPatterns" : [ { + "matchesJsonPath" : "$[?(@.name == 'Jan')]" + } ] + }, + "response" : { + "status" : 200, + "body" : "{\\"name\\":\\"Jan\\"}", + "headers" : { + "Content-Type" : "text/plain" + } + } +} +''', wireMockStub) + and: + 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: + 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')]" + } ] + }, + "response" : { + "status" : 200 + } +} + '''), wireMockStub) + 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: + AssertionUtil.assertThatJsonsAreEqual((''' +{ + "request" : { + "url" : "/users", + "method" : "GET", + "bodyPatterns" : [ { + "matchesJsonPath" : "$[?(@.name == 'Jan')]" + } ], + "headers" : { + "Content-Type" : { + "equalTo" : "customtype/json" + } + } + }, + "response" : { + "status" : 200 + } +} + '''), json) + 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: + AssertionUtil.assertThatJsonsAreEqual((''' + { + "request": { + "method": "GET", + "url": "/users", + "headers": { + "Content-Type": { + "equalTo": "customtype/xml" + } + }, + "bodyPatterns": [ + { + "equalToXml":"Jozo<test>" + } + ] + }, + "response": { + "status": 200 + } + } + '''), json) + 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: + AssertionUtil.assertThatJsonsAreEqual((''' + { + "request": { + "method": "GET", + "url": "/users", + "bodyPatterns": [ + { + "equalToXml":"Jozo<test>" + } + ] + }, + "response": { + "status": 200 + } + } + '''), json) + 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: + AssertionUtil.assertThatJsonsAreEqual((''' + { + "request": { + "method": "GET", + "url": "/users" + }, + "response": { + "status": 200, + "body":"Jozo<test>" + } + } + '''), json) + 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: + AssertionUtil.assertThatJsonsAreEqual((''' + { + "request": { + "method": "GET", + "url": "/users", + "bodyPatterns": [ + { + "equalToJson":"{\\"name\\":\\"Jan\\"}" + } + ] + }, + "response": { + "status": 200 + } + } + '''), json) + 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: + AssertionUtil.assertThatJsonsAreEqual((''' + { + "request": { + "method": "GET", + "url": "/users", + "bodyPatterns": [ + { + "equalToXml":"Jozo<test>" + } + ] + }, + "response": { + "status": 200 + } + } + '''), json) + 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 { + 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("""\ + { + "name": "Jan" + } + """ + ) + headers { + header 'Content-Type': 'text/plain' + } + } + } + when: + String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub() + then: + AssertionUtil.assertThatJsonsAreEqual((''' +{ + "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) + and: + 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: + 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" + } + } + }, + "response" : { + "status" : 200, + "body" : "{\\"fraudCheckStatus\\":\\"OK\\",\\"rejectionReason\\":null}", + "headers" : { + "Content-Type" : "application/vnd.fraud.v1+json" + } + } +} +'''), wireMockStub) + and: + stubMappingIsValidWireMockStub(wireMockStub) + } + + 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("10")) + parameter 'offset': $(client(containing("10")), server("10")) + parameter 'filter': "email" + 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")) + parameter 'credit': absent() + } + } + } + response { + status 200 + } + } + when: + def json = toWireMockClientJsonStub(groovyDsl) + then: + AssertionUtil.assertThatJsonsAreEqual((''' + { + "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.*" + }, + "credit": { + "absent": true + } + } + }, + "response": { + "status": 200, + } + } + '''), json) + and: + 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: + AssertionUtil.assertThatJsonsAreEqual((''' + { + "request": { + "method": "GET", + "urlPath": "boxes" + }, + "response": { + "status": 200, + } + } + '''), json) + 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: + AssertionUtil.assertThatJsonsAreEqual((''' + { + "request": { + "method": "GET", + "urlPath": "boxes" + }, + "response": { + "status": 200, + } + } + '''), json) + and: + 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.*") + } + } + } + response { + status 200 + } + } + then: + def e = thrown(IllegalStateException) + e.message.contains "Url can't be a pattern for the server side" + } + + 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 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 + } + } + 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 "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 { + 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: + AssertionUtil.assertThatJsonsAreEqual((''' + { + "request": { + "method": "GET", + "urlPattern": "users/[0-9]*", + "queryParameters": { + "age": { + "doesNotMatch": "^\\\\w*$" + }, + "name": { + "matches": "Denis.*" + } + } + }, + "response": { + "status": 200, + } + } + '''), json) + and: + stubMappingIsValidWireMockStub(json) + } + + 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: + 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 =~ /.*/)]" + } ] + }, + "response" : { + "status" : 200, + "body" : "{\\"name\\":\\"Jan\\"}", + "headers" : { + "Content-Type" : "text/plain" + } + } +} + '''), wireMockStub) + } + + 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: + 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 =~ /.*/)]" + } ] + }, + "response" : { + "status" : 200, + "body" : "{\\"status\\":\\"OK\\"}", + "headers" : { + "Content-Type" : "application/json" + } + } +} + '''), json) + } + + 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: + AssertionUtil.assertThatJsonsAreEqual((''' + { + "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: + AssertionUtil.assertThatJsonsAreEqual((''' + { + "priority": 9, + "request": { + "method": "POST", + "url": "test" + }, + "response": { + "status": 406 + } + } + '''), json) + } + + @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: + AssertionUtil.assertThatJsonsAreEqual((''' +{ + "request" : { + "method" : "POST", + "bodyPatterns" : [ { + "matchesJsonPath" : "$[?(@.property == 'value')]" + } ] + }, + "response" : { + "status" : 200 + } +} + '''), wireMockStub) + and: + stubMappingIsValidWireMockStub(wireMockStub) + } + + @Issue("#121") + 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: + AssertionUtil.assertThatJsonsAreEqual((''' + { + "request": { + "method": "POST", + "bodyPatterns": [ + { + "equalToJson": "{\\"values\\":[]}" + } + ] + }, + "response": { + "status": 200 + } + } + '''), wireMockStub) + and: + 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) + } + + @Issue('42') + @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')) + ) + } + 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(stub(123123), test(optional(123123)))}", + "message" : "User not found by email = [${value(server(regex(email())), client('not.existing@user.com'))}]" + } + """ + ) + } + } + ] + } + + String toJsonString(value) { + new JsonBuilder(value).toPrettyString() + } + + Object parseJson(json) { + new JsonSlurper().parseText(json) + } + + String toWireMockClientJsonStub(groovyDsl) { + new WireMockStubStrategy(groovyDsl).toWireMockClientStub() + } +} 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 new file mode 100644 index 0000000000..537fb78ea2 --- /dev/null +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockStubVerifier.groovy @@ -0,0 +1,16 @@ +package io.codearte.accurest.dsl +import com.github.tomakehurst.wiremock.stubbing.StubMapping + +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('io.codearte.accurest.dsl.internal') + } + +} 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 13299ceebe..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 deleted file mode 100755 index 4ff7fa09f5..0000000000 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockGroovyDslSpec.groovy +++ /dev/null @@ -1,289 +0,0 @@ -package io.codearte.accurest.dsl -import groovy.json.JsonSlurper - -class WiremockGroovyDslSpec extends WiremockSpec { - - def 'should convert groovy dsl stub to wiremock stub for the client side'() { - given: - GroovyDsl groovyDsl = GroovyDsl.make { - request { - method('GET') - url $(client(~/\/[0-9]{2}/), server('/12')) - } - response { - status 200 - 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) })) - ) - 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}" - }, - "response": { - "status": 200, - "body": "{\\"id\\":\\"123\\",\\"surname\\":\\"Kowalsky\\",\\"name\\":\\"Jan\\",\\"created\\":\\"2014-02-02 12:23:43\\"}", - "headers": { - "Content-Type": "text/plain" - } - } -} -''') - 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 { - request { - method('GET') - url $(client(~/\/[0-9]{2}/), server('/12')) - } - 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'))}" - } - """ - ) - 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}" - }, - "response": { - "status": 200, - "body": "{\\"created\\":\\"2014-02-02 12:23:43\\",\\"id\\":\\"123\\",\\"name\\":\\"Jan\\",\\"surname\\":\\"Kowalsky\\"}", - "headers": { - "Content-Type": "text/plain" - } - } -} -''') - and: - stubMappingIsValidWiremockStub(wiremockStub) - } - - def 'should convert groovy dsl stub with simple 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 """ - { - "name": "Jan" - } - """ - } - 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": [ - { - "equalTo":"{\\"name\\":\\"Jan\\"}" - } - ] - }, - "response": { - "status": 200, - "body": "{\\"name\\":\\"Jan\\"}", - "headers": { - "Content-Type": "text/plain" - } - } -} -''') - and: - 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 { - 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("""\ - { - "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":"{\\"personalId\\":\\"^[0-9]{11}$\\"}" - } - ] - }, - "response": { - "status": 200, - "body": "{\\"name\\":\\"Jan\\"}", - "headers": { - "Content-Type": "text/plain" - } - } -} -''') - 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 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.*$" - } - } - } - ''') - } -} 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 deleted file mode 100644 index 2a6237d89d..0000000000 --- a/accurest-core/src/test/groovy/io/codearte/accurest/dsl/WiremockSpec.groovy +++ /dev/null @@ -1,12 +0,0 @@ -package io.codearte.accurest.dsl - -import com.github.tomakehurst.wiremock.stubbing.StubMapping -import spock.lang.Specification - -class WiremockSpec extends Specification { - - void stubMappingIsValidWiremockStub(String mappingDefinition) { - StubMapping.buildFrom(mappingDefinition) - } - -} 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/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..bc217f336e --- /dev/null +++ b/accurest-core/src/test/groovy/io/codearte/accurest/dsl/internal/RegexPatternsSpec.groovy @@ -0,0 +1,68 @@ +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://localhost' || true + 'https://localhost: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 + } + + @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 + } +} diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/util/JsonToJsonPathsConverterSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/util/JsonToJsonPathsConverterSpec.groovy new file mode 100644 index 0000000000..5348c77fd5 --- /dev/null +++ b/accurest-core/src/test/groovy/io/codearte/accurest/util/JsonToJsonPathsConverterSpec.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 JsonToJsonPathsConverterSpec extends Specification { + + @Unroll + def 'should convert a json with list as root to a map of path to value'() { + when: + JsonPaths pathAndValues = JsonToJsonPathsConverter.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 = JsonToJsonPathsConverter.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 = JsonToJsonPathsConverter.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 = JsonToJsonPathsConverter.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 = JsonToJsonPathsConverter.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-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/main/groovy/io/codearte/accurest/plugin/AccurestGradlePlugin.groovy b/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/AccurestGradlePlugin.groovy index cee3f48357..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 @@ -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 @@ -11,7 +12,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 +28,9 @@ class AccurestGradlePlugin implements Plugin { setConfigurationDefaults(extension) createGenerateTestsTask(extension) - createAndConfigureGenerateWiremockClientStubsFromDslTask(extension) + createAndConfigureGenerateWireMockClientStubsFromDslTask(extension) + deprecatedCreateAndConfigureGenerateWiremockClientStubsFromDslTask() + project.dependencies.add("testCompile", "com.github.tomakehurst:wiremock:2.0.5-beta") project.afterEvaluate { def hasIdea = project.plugins.findPlugin(IDEA_PLUGIN_CLASS) @@ -60,13 +63,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 63% 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 6a6f08f692..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() { - 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(), + 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 e2ee36959d..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 @@ -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,28 +38,29 @@ 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(""" -{ - "request": { - "method": "PUT", - "headers": { - "Content-Type": { - "equalTo": "application/json" - } - }, - "url": "/api/12", - "bodyPatterns": [ - { "equalTo": "[{\\"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"() { @@ -67,16 +68,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 +85,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/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/groovy/io/codearte/accurest/plugin/SampleProjectSpec.groovy b/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/SampleProjectSpec.groovy new file mode 100755 index 0000000000..e32b8810f4 --- /dev/null +++ b/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/SampleProjectSpec.groovy @@ -0,0 +1,21 @@ +package io.codearte.accurest.plugin + +import nebula.test.IntegrationSpec +import spock.lang.Stepwise + +@Stepwise +class SampleProjectSpec extends IntegrationSpec { + + void setup() { + copyResources("functionalTest/sampleProject", "") + 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/bootSimple/build.gradle b/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/build.gradle index 1241194253..e7565e3efe 100644 --- a/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/build.gradle +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/build.gradle @@ -1,75 +1,66 @@ buildscript { - repositories { - mavenCentral() - } - dependencies { - classpath "io.codearte.accurest:accurest-gradle-plugin:$accurestVersion" - } + 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.5" + compile 'com.fasterxml.jackson.core:jackson-databind:2.4.4' + 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.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" + 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 "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() +task createWireMockStubsOutputDir << { + wireMockStubsOutputDir.mkdirs() } -generateWiremockClientStubs.dependsOn { createWiremockStubsOutputDir } -generateAccurest.dependsOn generateWiremockClientStubs +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/gradle.properties b/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/gradle.properties index 6e02ac3599..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,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 +springVersion=4.1.7.RELEASE \ 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/colleratePlacesFromTweet.groovy b/accurest-gradle-plugin/src/test/resources/functionalTest/bootSimple/repository/mappings/com/ofg/twitter-places-analyzer/pairId/colleratePlacesFromTweet.groovy index 08b137a054..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,17 +1,18 @@ io.codearte.accurest.dsl.GroovyDsl.make { - request { - method 'PUT' - url '/api/12' - headers { - header 'Content-Type': 'application/json' - } - body '''\ - [{ - "text": "Gonna see you at Warsaw" - }] + priority 2 + 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/build.gradle b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/build.gradle new file mode 100644 index 0000000000..f33b17b5a6 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/build.gradle @@ -0,0 +1,94 @@ +buildscript { + repositories { + mavenLocal() + mavenCentral() + } + dependencies { + classpath "org.springframework.boot:spring-boot-gradle-plugin:1.2.6.RELEASE" + } +} + +ext { + restAssuredVersion = '2.5.0' + spockVersion = '1.0-groovy-2.4' + wiremockVersion = '2.0.5-beta' + + accurestStubsBaseDirectory = 'src/test/resources/stubs' +} + +subprojects { + apply plugin: 'groovy' + + repositories { + mavenCentral() + mavenLocal() + } + + dependencies { + 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:$wiremockVersion" + } +} + +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' + } + + configurations { + compile.exclude module: "spring-boot-starter-tomcat" + } + + dependencies { + compile 'org.glassfish.jersey.containers:jersey-container-jetty-http:2.15' + 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' + } + + 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..44b1c08604 --- /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..7bc64d0dac --- /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..83e15edc59 --- /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 0000000000..667288ad6c Binary files /dev/null and b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradle/wrapper/gradle-wrapper.jar differ diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradle/wrapper/gradle-wrapper.properties b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..b4603dcb69 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Jan 28 00:32:44 CET 2015 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=http\://services.gradle.org/distributions/gradle-2.4-all.zip diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradlew b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradlew new file mode 100755 index 0000000000..91a7e269e1 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + 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..7229872299 --- /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..5a251171c7 --- /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' diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/build.gradle b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/build.gradle new file mode 100644 index 0000000000..25ca064fc4 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/build.gradle @@ -0,0 +1,92 @@ +buildscript { + repositories { + mavenCentral() + mavenLocal() + } + dependencies { + classpath("org.springframework.boot:spring-boot-gradle-plugin:1.2.6.RELEASE") + } +} + +ext { + restAssuredVersion = '2.5.0' + spockVersion = '1.0-groovy-2.4' + wiremockVersion = '2.0.5-beta' + + accurestStubsBaseDirectory = 'src/test/resources/stubs' +} + +subprojects { + apply plugin: 'groovy' + + repositories { + mavenCentral() + mavenLocal() + } + + dependencies { + 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:$wiremockVersion" + } +} + +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/sampleProject/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsFraud.groovy b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsFraud.groovy new file mode 100644 index 0000000000..44b1c08604 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/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/sampleProject/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.groovy b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.groovy new file mode 100644 index 0000000000..7bc64d0dac --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/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/sampleProject/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 new file mode 100644 index 0000000000..5a1a60244e --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/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/sampleProject/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 new file mode 100644 index 0000000000..e264462cce --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/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/sampleProject/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 new file mode 100644 index 0000000000..77471aee19 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/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/sampleProject/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 new file mode 100644 index 0000000000..28efc573f5 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/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/sampleProject/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 new file mode 100644 index 0000000000..b87c365d51 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/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/sampleProject/fraudDetectionService/src/main/resources/application.yml b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/fraudDetectionService/src/main/resources/application.yml new file mode 100644 index 0000000000..a30a91f034 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/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/sampleProject/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 new file mode 100644 index 0000000000..bcb6ef1579 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/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/sampleProject/gradle/wrapper/gradle-wrapper.jar b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000..667288ad6c Binary files /dev/null and b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/gradle/wrapper/gradle-wrapper.jar differ diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/gradle/wrapper/gradle-wrapper.properties b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..b4603dcb69 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Jan 28 00:32:44 CET 2015 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=http\://services.gradle.org/distributions/gradle-2.4-all.zip diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/gradlew b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/gradlew new file mode 100755 index 0000000000..91a7e269e1 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + 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/sampleProject/gradlew.bat b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/gradlew.bat new file mode 100644 index 0000000000..8a0b282aa6 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/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/sampleProject/loanApplicationService/mappings/.gitkeep b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/loanApplicationService/mappings/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/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 new file mode 100644 index 0000000000..5a1a60244e --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/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/sampleProject/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 new file mode 100644 index 0000000000..82476a3a46 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/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/sampleProject/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 new file mode 100644 index 0000000000..5e91273eda --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/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/sampleProject/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 new file mode 100644 index 0000000000..b87c365d51 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/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/sampleProject/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 new file mode 100644 index 0000000000..ac595998bc --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/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/sampleProject/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 new file mode 100644 index 0000000000..9f3353ecbf --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/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/sampleProject/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 new file mode 100644 index 0000000000..816087988b --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/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/sampleProject/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 new file mode 100644 index 0000000000..523f4f2ea3 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/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/sampleProject/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 new file mode 100644 index 0000000000..7f7f86e0ea --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/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/sampleProject/loanApplicationService/src/main/resources/application.yml b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/loanApplicationService/src/main/resources/application.yml new file mode 100644 index 0000000000..e86bbd0e0f --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/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/sampleProject/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 new file mode 100644 index 0000000000..58d17673ce --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/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/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 new file mode 100644 index 0000000000..157726ca2e --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/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/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 new file mode 100644 index 0000000000..afa27159d9 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/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/sampleProject/settings.gradle b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/settings.gradle new file mode 100644 index 0000000000..6a42a6c7ce --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/sampleProject/settings.gradle @@ -0,0 +1,2 @@ +include ':fraudDetectionService' +include ':loanApplicationService' 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 c2d42cfdf1..42d265f044 100644 --- a/build.gradle +++ b/build.gradle @@ -1,20 +1,16 @@ buildscript { repositories { mavenCentral() + mavenLocal() } 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 "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" } @@ -22,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'" }] } } @@ -31,13 +27,16 @@ allprojects { project.version = scmVersion.version } +apply plugin: 'io.codearte.nexus-staging' + +nexusStaging { + packageGroup = "io.codearte" + stagingProfileId = '93c08fdebde1ff' +} + 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' @@ -47,6 +46,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/ @@ -55,49 +55,46 @@ 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') } } - - 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') { + 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.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.github.tomakehurst:wiremock:$wiremockVersion" testCompile 'cglib:cglib-nodep:2.2' testCompile 'org.objenesis:objenesis:2.1' - testCompile 'com.github.tomakehurst:wiremock:1.53' + testCompile project(':accurest-testing-utils') } + +} + +project(':accurest-testing-utils') { + + dependencies { + compile 'org.skyscreamer:jsonassert:1.2.3' + } + } 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:$wiremockVersion" + testCompile 'org.hamcrest:hamcrest-all:1.3' } } @@ -106,10 +103,10 @@ project(':accurest-gradle-plugin') { compile project(':accurest-core') 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') } + testCompile project(':accurest-testing-utils') } test { @@ -127,5 +124,5 @@ project(':accurest-gradle-plugin') { } task wrapper(type: Wrapper) { - gradleVersion = '2.2.1' + gradleVersion = '2.4' } diff --git a/gradle.properties b/gradle.properties index fe3cdf2fd8..27a71580f7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,4 @@ nexusUsername = -nexusPassword = \ No newline at end of file +nexusPassword = + +wiremockVersion = 2.0.5-beta diff --git a/gradle/release.gradle b/gradle/release.gradle index 34e8ae08ef..e3f5159bb1 100644 --- a/gradle/release.gradle +++ b/gradle/release.gradle @@ -1,64 +1,40 @@ -apply plugin: 'maven' -apply plugin: 'signing' +apply plugin: 'com.bmuschko.nexus' -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) + developers { + developer { + id 'jkubrynski' + name 'Jakub Kubrynski' + email 'jk ATT codearte DOTT io' } - - 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' - } - } + developer { + id 'marcingrzejszczak' + name 'Marcin Grzejszczak' + email 'marcin ATT grzejszczak DOTT pl' } } } -} \ No newline at end of file +} + +uploadArchives.dependsOn { check } + 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 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"