Merge remote-tracking branch 'upstream/master'
Conflicts: accurest-core/src/main/groovy/io/codearte/accurest/TestGenerator.groovy
This commit is contained in:
@@ -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
|
||||
script: ./gradlew check funcTest --stacktrace --info --continue
|
||||
|
||||
15
README.md
15
README.md
@@ -2,13 +2,20 @@ Accurate REST
|
||||
=============
|
||||
|
||||
[](https://travis-ci.org/Codearte/accurest) [](https://maven-badges.herokuapp.com/maven-central/io.codearte.accurest/accurest-gradle-plugin)
|
||||
[](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 :)
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
"""
|
||||
}
|
||||
118
accurest-converters/src/main/groovy/nl/flotsam/xeger/Xeger.java
Normal file
118
accurest-converters/src/main/groovy/nl/flotsam/xeger/Xeger.java
Normal file
@@ -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 <code>null</code>.)
|
||||
* @param random The object that will randomize the way the String is generated. (Not <code>null</code>.)
|
||||
* @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<Transition> 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;
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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}
|
||||
}
|
||||
""")
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<String> firstRegexList = generateRegex(generator, 100);
|
||||
List<String> secondRegexList = generateRegex(generator2, 100);
|
||||
|
||||
for (int i = 0; i < firstRegexList.size(); i++) {
|
||||
assertEquals("Index mismatch: " + i, firstRegexList.get(i),
|
||||
secondRegexList.get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> generateRegex(Xeger generator, int count) {
|
||||
List<String> regexList = new ArrayList<String>();
|
||||
for (int i = 0; i < count; i++) {
|
||||
regexList.add(generator.generate());
|
||||
}
|
||||
return regexList;
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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'])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,11 @@ class ClassBuilder {
|
||||
return this
|
||||
}
|
||||
|
||||
ClassBuilder addImport(List<String> importsToAdd) {
|
||||
imports.addAll(importsToAdd)
|
||||
return this
|
||||
}
|
||||
|
||||
ClassBuilder addStaticImport(String importToAdd) {
|
||||
staticImports << importToAdd
|
||||
return this
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<String>) { List<String> result, QueryParameter param ->
|
||||
result << "${param.name}=${resolveParamValue(param).toString()}"
|
||||
}
|
||||
.join('&')
|
||||
}
|
||||
return "$urlPath.serverValue?$params"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -4,5 +4,5 @@ package io.codearte.accurest.config
|
||||
* @author Jakub Kubrynski
|
||||
*/
|
||||
enum TestMode {
|
||||
MOCKMVC, EXPLICIT
|
||||
MOCKMVC, EXPLICIT, JAXRSCLIENT
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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<String, Object> buildRequestContent(ClientRequest request) {
|
||||
return ([method : request?.method?.clientValue,
|
||||
headers : buildClientRequestHeadersSection(request.headers)
|
||||
] << appendUrl(request) << appendBody(request)).findAll { it.value }
|
||||
}
|
||||
|
||||
private Map<String, Object> appendUrl(ClientRequest clientRequest) {
|
||||
Object url = clientRequest?.url?.clientValue
|
||||
return url instanceof Pattern ? [urlPattern: ((Pattern)url).pattern()] : [url: url]
|
||||
}
|
||||
|
||||
private Map<String, Object> 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()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<String, Object> buildResponseContent(ClientResponse response) {
|
||||
return ([status : response?.status?.clientValue,
|
||||
headers: buildClientResponseHeadersSection(response.headers)
|
||||
] << appendBody(response)).findAll { it.value }
|
||||
}
|
||||
|
||||
private Map<String, Object> appendBody(ClientResponse response) {
|
||||
Object body = response?.body?.clientValue
|
||||
return body != null ? [body: parseBody(body)] : [:]
|
||||
}
|
||||
}
|
||||
@@ -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()]))
|
||||
}
|
||||
}
|
||||
@@ -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<String, DslProperty> 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<String, Object> extractValue(Map<String, DslProperty> body, Closure valueProvider) {
|
||||
@@ -19,8 +19,8 @@ class Body extends DslProperty {
|
||||
} as Map<String, Object>
|
||||
}
|
||||
|
||||
Body(List bodyAsList) {
|
||||
super(bodyAsList.collect { it.clientValue }, bodyAsList.collect { it.serverValue })
|
||||
Body(List<DslProperty> 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)
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ import java.util.regex.Pattern
|
||||
@PackageScope
|
||||
class Common {
|
||||
|
||||
@Delegate private final RegexPatterns regexPatterns = new RegexPatterns()
|
||||
|
||||
Map<String, DslProperty> convertObjectsToDslProperties(Map<String, Object> body) {
|
||||
return body.collectEntries {
|
||||
Map.Entry<String, Object> entry ->
|
||||
@@ -20,10 +22,10 @@ class Common {
|
||||
} as Map<String, DslProperty>
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<Boolean> retrievePlaceholders,
|
||||
Closure<String> performAdditionalLogicOnSerializedJson,
|
||||
Closure convertSerializedJsonToSth) {
|
||||
LinkedList<Object> 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)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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)?"
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<QueryParameter> parameters = []
|
||||
|
||||
void parameter(Map<String, Object> singleParameter) {
|
||||
Map.Entry<String, Object> first = singleParameter.entrySet().first()
|
||||
parameters << new QueryParameter(first?.key, first?.value)
|
||||
}
|
||||
|
||||
void parameter(String parameterName, Object parameterValue) {
|
||||
parameters << new QueryParameter(parameterName, parameterValue)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* If you wonder why there is val[1] without null-check then take a look at this:
|
||||
* </p>
|
||||
* <p>
|
||||
* Example:
|
||||
* </p>
|
||||
* <p>
|
||||
* Our string equals: {@code EXECUTION>>assertThatRejectionReasonIsNull($it)<<}
|
||||
* The matcher matches this group with the pattern {@code EXECUTION>>(.*)<<}
|
||||
* </p>
|
||||
* <p>
|
||||
* So {@code executionMatcher[0]} returns 2 elements:
|
||||
* <ul>
|
||||
* <li> index0: EXECUTION>>assertThatRejectionReasonIsNull($it)<< </li>
|
||||
* <li> index1: assertThatRejectionReasonIsNull($it)<< </li>
|
||||
* </ul>
|
||||
* </p>
|
||||
* <p>
|
||||
* Thus one can safely write {@code executionMatcher[0][1]} to retrieve the matched group
|
||||
* </p>
|
||||
* @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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<String> 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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package io.codearte.accurest.util
|
||||
|
||||
class JsonPaths extends HashSet<JsonPathEntry> {
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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'"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<String, Object> 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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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\"")
|
||||
}
|
||||
}
|
||||
1401
accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy
Executable file
1401
accurest-core/src/test/groovy/io/codearte/accurest/dsl/WireMockGroovyDslSpec.groovy
Executable file
File diff suppressed because it is too large
Load Diff
@@ -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')
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
''')
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.*$"
|
||||
}
|
||||
}
|
||||
}
|
||||
''')
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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'
|
||||
|
||||
@@ -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<Project> {
|
||||
|
||||
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<Project> {
|
||||
|
||||
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<Project> {
|
||||
}
|
||||
}
|
||||
|
||||
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.")}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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')
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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'
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
groupId=com.ofg
|
||||
jacksonMapper=1.9.13
|
||||
restAssuredVersion=2.4.0
|
||||
accurestVersion=0.4.1
|
||||
springVersion=4.1.4.RELEASE
|
||||
springVersion=4.1.7.RELEASE
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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<com.ofg.twitter.place.Tweet> 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<com.ofg.twitter.place.Tweet> 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
|
||||
}
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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')
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<Class<?>> getClasses() {
|
||||
return Collections.<Class<?>>singleton(FraudDetectionController.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.blogspot.toomuchcoding.frauddetection.model;
|
||||
|
||||
public enum FraudCheckStatus {
|
||||
OK, FRAUD
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
server.port=8085
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -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
|
||||
164
accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradlew
vendored
Executable file
164
accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradlew
vendored
Executable file
@@ -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 "$@"
|
||||
90
accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradlew.bat
vendored
Normal file
90
accurest-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradlew.bat
vendored
Normal file
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<FraudServiceResponse> 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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user