Merge branch 'jersey-tests-support' of https://github.com/dstepanov/accurest into dstepanov-jersey-tests-support
# Conflicts: # accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy
This commit is contained in:
@@ -11,6 +11,7 @@ import static io.codearte.accurest.builder.MethodBuilder.createTestMethod
|
||||
import static io.codearte.accurest.util.NamesUtil.capitalize
|
||||
|
||||
class SingleTestGenerator {
|
||||
|
||||
private final AccurestConfigProperties configProperties
|
||||
|
||||
SingleTestGenerator(AccurestConfigProperties configProperties) {
|
||||
@@ -34,7 +35,9 @@ class SingleTestGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
if (configProperties.testMode == TestMode.MOCKMVC) {
|
||||
if (configProperties.testMode == TestMode.JAXRSCLIENT) {
|
||||
clazz.addStaticImport('javax.ws.rs.client.Entity.*')
|
||||
} else if (configProperties.testMode == TestMode.MOCKMVC) {
|
||||
clazz.addStaticImport('com.jayway.restassured.module.mockmvc.RestAssuredMockMvc.*')
|
||||
} else {
|
||||
clazz.addStaticImport('com.jayway.restassured.RestAssured.*')
|
||||
@@ -52,7 +55,7 @@ class SingleTestGenerator {
|
||||
}
|
||||
|
||||
listOfFiles.each {
|
||||
clazz.addMethod(createTestMethod(it, configProperties.targetFramework))
|
||||
clazz.addMethod(createTestMethod(it, configProperties))
|
||||
}
|
||||
return clazz.build()
|
||||
}
|
||||
|
||||
@@ -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,7 +1,9 @@
|
||||
package io.codearte.accurest.builder
|
||||
|
||||
import groovy.util.logging.Slf4j
|
||||
import io.codearte.accurest.config.AccurestConfigProperties
|
||||
import io.codearte.accurest.config.TestFramework
|
||||
import io.codearte.accurest.config.TestMode
|
||||
import io.codearte.accurest.dsl.GroovyDsl
|
||||
import io.codearte.accurest.util.NamesUtil
|
||||
|
||||
@@ -13,28 +15,36 @@ class MethodBuilder {
|
||||
|
||||
private final String methodName
|
||||
private final GroovyDsl stubContent
|
||||
private final TestFramework lang
|
||||
private final AccurestConfigProperties configProperties
|
||||
|
||||
private MethodBuilder(String methodName, GroovyDsl stubContent, TestFramework lang) {
|
||||
private MethodBuilder(String methodName, GroovyDsl stubContent, AccurestConfigProperties configProperties) {
|
||||
this.stubContent = stubContent
|
||||
this.methodName = methodName
|
||||
this.lang = lang
|
||||
this.configProperties = configProperties
|
||||
}
|
||||
|
||||
static MethodBuilder createTestMethod(File stubsFile, TestFramework lang) {
|
||||
static MethodBuilder createTestMethod(File stubsFile, AccurestConfigProperties configProperties) {
|
||||
log.debug("Stub content from file [${stubsFile.text}]")
|
||||
GroovyDsl stubContent = new GroovyShell(this.classLoader).evaluate(stubsFile)
|
||||
log.debug("Stub content Groovy DSL [$stubContent]")
|
||||
String methodName = NamesUtil.camelCase(NamesUtil.toLastDot(NamesUtil.afterLast(stubsFile.path, File.separator)))
|
||||
return new MethodBuilder(methodName, stubContent, lang)
|
||||
return new MethodBuilder(methodName, stubContent, configProperties)
|
||||
}
|
||||
|
||||
void appendTo(BlockBuilder blockBuilder) {
|
||||
if (lang == TestFramework.JUNIT) {
|
||||
if (configProperties.targetFramework == TestFramework.JUNIT) {
|
||||
blockBuilder.addLine('@Test')
|
||||
}
|
||||
blockBuilder.addLine(lang.methodModifier + "$methodName() {")
|
||||
new SpockMethodBodyBuilder(stubContent).appendTo(blockBuilder)
|
||||
blockBuilder.addLine(configProperties.targetFramework.methodModifier + "$methodName() {")
|
||||
getMethodBodyBuilder().appendTo(blockBuilder)
|
||||
blockBuilder.addLine('}')
|
||||
}
|
||||
|
||||
private SpockMethodBodyBuilder getMethodBodyBuilder() {
|
||||
if (configProperties.testMode == TestMode.JAXRSCLIENT) {
|
||||
return new JaxRsClientSpockMethodBodyBuilder(stubContent)
|
||||
}
|
||||
return new MockMvcSpockMethodBodyBuilder(stubContent)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package io.codearte.accurest.builder
|
||||
|
||||
import groovy.transform.PackageScope
|
||||
import groovy.transform.TypeChecked
|
||||
import groovy.transform.TypeCheckingMode
|
||||
import io.codearte.accurest.dsl.GroovyDsl
|
||||
import io.codearte.accurest.dsl.internal.Header
|
||||
import io.codearte.accurest.dsl.internal.QueryParameter
|
||||
import io.codearte.accurest.dsl.internal.Request
|
||||
import io.codearte.accurest.dsl.internal.UrlPath
|
||||
|
||||
@PackageScope
|
||||
@TypeChecked
|
||||
class MockMvcSpockMethodBodyBuilder extends SpockMethodBodyBuilder {
|
||||
|
||||
MockMvcSpockMethodBodyBuilder(GroovyDsl stubDefinition) {
|
||||
super(stubDefinition)
|
||||
}
|
||||
|
||||
protected void given(BlockBuilder bb) {
|
||||
bb.addLine('def request = given()')
|
||||
bb.indent()
|
||||
request.headers?.collect { Header header ->
|
||||
bb.addLine(".header('${header.name}', '${header.serverValue}')")
|
||||
}
|
||||
if (request.body) {
|
||||
bb.addLine(".body('$bodyAsString')")
|
||||
}
|
||||
bb.unindent()
|
||||
}
|
||||
|
||||
protected void when(BlockBuilder bb) {
|
||||
bb.addLine('def response = given().spec(request)')
|
||||
bb.indent()
|
||||
|
||||
String url = buildUrl(request)
|
||||
String method = request.method.serverValue.toString().toLowerCase()
|
||||
|
||||
bb.addLine(/.${method}("$url")/)
|
||||
bb.unindent()
|
||||
}
|
||||
|
||||
protected void validateResponseCodeBlock(BlockBuilder bb) {
|
||||
bb.addLine("response.statusCode == $response.status.serverValue")
|
||||
}
|
||||
|
||||
protected void validateResponseHeadersBlock(BlockBuilder bb) {
|
||||
response.headers?.collect { Header header ->
|
||||
bb.addLine("response.header('$header.name') == '$header.serverValue'")
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getResponseAsString() {
|
||||
return 'response.body.asString()'
|
||||
}
|
||||
|
||||
protected String buildUrl(Request request) {
|
||||
if (request.url)
|
||||
return request.url.serverValue;
|
||||
if (request.urlPath)
|
||||
return buildUrlFromUrlPath(request.urlPath)
|
||||
throw new IllegalStateException("URL is not set!")
|
||||
}
|
||||
|
||||
@TypeChecked(TypeCheckingMode.SKIP)
|
||||
protected String buildUrlFromUrlPath(UrlPath urlPath) {
|
||||
String params = urlPath.queryParameters.parameters
|
||||
.findAll(this.&allowedQueryParameter)
|
||||
.inject([] as List<String>) { List<String> result, QueryParameter param ->
|
||||
result << "${param.name}=${resolveParamValue(param).toString()}"
|
||||
}
|
||||
.join('&')
|
||||
return "$urlPath.serverValue?$params"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,16 +1,14 @@
|
||||
package io.codearte.accurest.builder
|
||||
|
||||
import groovy.json.JsonOutput
|
||||
import groovy.transform.PackageScope
|
||||
import groovy.transform.TypeChecked
|
||||
import io.codearte.accurest.dsl.GroovyDsl
|
||||
import io.codearte.accurest.dsl.internal.DslProperty
|
||||
import io.codearte.accurest.dsl.internal.ExecutionProperty
|
||||
import io.codearte.accurest.dsl.internal.Header
|
||||
import io.codearte.accurest.dsl.internal.MatchingStrategy
|
||||
import io.codearte.accurest.dsl.internal.QueryParameter
|
||||
import io.codearte.accurest.dsl.internal.Request
|
||||
import io.codearte.accurest.dsl.internal.Response
|
||||
import io.codearte.accurest.dsl.internal.UrlPath
|
||||
import io.codearte.accurest.util.ContentType
|
||||
import io.codearte.accurest.util.JsonConverter
|
||||
|
||||
@@ -24,87 +22,95 @@ import static io.codearte.accurest.util.ContentUtils.recognizeContentTypeFromHea
|
||||
* @author Jakub Kubrynski
|
||||
*/
|
||||
@PackageScope
|
||||
class SpockMethodBodyBuilder {
|
||||
private final GroovyDsl stubDefinition
|
||||
@TypeChecked
|
||||
abstract class SpockMethodBodyBuilder {
|
||||
|
||||
protected final Request request
|
||||
protected final Response response
|
||||
|
||||
SpockMethodBodyBuilder(GroovyDsl stubDefinition) {
|
||||
this.stubDefinition = stubDefinition
|
||||
this.request = stubDefinition.request
|
||||
this.response = stubDefinition.response
|
||||
}
|
||||
|
||||
void appendTo(BlockBuilder blockBuilder) {
|
||||
Request request = stubDefinition.request
|
||||
Response response = stubDefinition.response
|
||||
blockBuilder.with {
|
||||
startBlock()
|
||||
addLine('given:').startBlock()
|
||||
addLine('def request = given()')
|
||||
indent()
|
||||
request.headers?.collect { Header header ->
|
||||
addLine(".header('${header.name}', '${header.serverValue}')")
|
||||
}
|
||||
if (request.body) {
|
||||
Object bodyValue = extractServerValueFromBody(request.body.serverValue)
|
||||
String matches = trimRepeatedQuotes(new JsonOutput().toJson(bodyValue))
|
||||
addLine(".body('$matches')")
|
||||
}
|
||||
blockBuilder.startBlock()
|
||||
|
||||
unindent().endBlock().addEmptyLine()
|
||||
givenBlock(blockBuilder)
|
||||
whenBlock(blockBuilder)
|
||||
thenBlock(blockBuilder)
|
||||
|
||||
addLine('when:').startBlock()
|
||||
addLine('def response = given().spec(request)')
|
||||
indent()
|
||||
blockBuilder.endBlock()
|
||||
}
|
||||
|
||||
String url = buildUrl(request)
|
||||
String method = request.method.serverValue.toLowerCase()
|
||||
protected void thenBlock(BlockBuilder bb) {
|
||||
bb.addLine('then:')
|
||||
bb.startBlock()
|
||||
then(bb)
|
||||
bb.endBlock()
|
||||
}
|
||||
|
||||
blockBuilder.addLine(/.${method}("$url")/)
|
||||
unindent().endBlock().addEmptyLine()
|
||||
protected void whenBlock(BlockBuilder bb) {
|
||||
bb.addLine('when:')
|
||||
bb.startBlock()
|
||||
when(bb)
|
||||
bb.endBlock().addEmptyLine()
|
||||
}
|
||||
|
||||
addLine('then:').startBlock()
|
||||
addLine("response.statusCode == $response.status.serverValue")
|
||||
protected void givenBlock(BlockBuilder bb) {
|
||||
bb.addLine('given:')
|
||||
bb.startBlock()
|
||||
given(bb)
|
||||
bb.endBlock().addEmptyLine()
|
||||
}
|
||||
|
||||
response.headers?.collect { Header header ->
|
||||
addLine("response.header('$header.name') == '$header.serverValue'")
|
||||
}
|
||||
if (response.body) {
|
||||
endBlock()
|
||||
addLine('and:').startBlock()
|
||||
def responseBody = response.body.serverValue
|
||||
ContentType contentType = recognizeContentTypeFromHeader(response.headers)
|
||||
if (contentType == ContentType.UNKNOWN) {
|
||||
contentType = recognizeContentTypeFromContent(responseBody)
|
||||
}
|
||||
if (responseBody instanceof GString) {
|
||||
responseBody = extractValue(responseBody, contentType, { DslProperty dslProperty -> dslProperty.serverValue })
|
||||
}
|
||||
if (contentType == ContentType.JSON) {
|
||||
addLine('def responseBody = new JsonSlurper().parseText(response.body.asString())')
|
||||
if (responseBody instanceof List) {
|
||||
processArrayElements(responseBody, "", blockBuilder)
|
||||
} else if (responseBody instanceof Map) {
|
||||
processMapElement(responseBody, blockBuilder, "")
|
||||
} else {
|
||||
processBodyElement(blockBuilder, '', responseBody)
|
||||
}
|
||||
} else if (contentType == ContentType.XML) {
|
||||
addLine('def responseBody = new XmlSlurper().parseText(response.body.asString())')
|
||||
// TODO xml validation
|
||||
}
|
||||
}
|
||||
endBlock()
|
||||
protected void given(BlockBuilder bb) {}
|
||||
|
||||
endBlock()
|
||||
protected abstract void when(BlockBuilder bb)
|
||||
|
||||
protected abstract void validateResponseCodeBlock(BlockBuilder bb)
|
||||
|
||||
protected abstract void validateResponseHeadersBlock(BlockBuilder bb)
|
||||
|
||||
protected abstract String getResponseAsString()
|
||||
|
||||
protected void then(BlockBuilder bb) {
|
||||
validateResponseCodeBlock(bb)
|
||||
if (response.headers) {
|
||||
validateResponseHeadersBlock(bb)
|
||||
}
|
||||
if (response.body) {
|
||||
bb.endBlock()
|
||||
bb.addLine('and:').startBlock()
|
||||
validateResponseBodyBlock(bb)
|
||||
}
|
||||
}
|
||||
|
||||
private String trimRepeatedQuotes(String toTrim) {
|
||||
if (toTrim.startsWith('"')) {
|
||||
return toTrim.replaceAll('"', '')
|
||||
protected void validateResponseBodyBlock(BlockBuilder bb) {
|
||||
def responseBody = response.body.serverValue
|
||||
ContentType contentType = getResponseContentType()
|
||||
if (responseBody instanceof GString) {
|
||||
responseBody = extractValue(responseBody, contentType, { DslProperty dslProperty -> dslProperty.serverValue })
|
||||
}
|
||||
if (contentType == ContentType.JSON) {
|
||||
bb.addLine("def responseBody = new JsonSlurper().parseText($responseAsString)")
|
||||
processBodyElement(bb, "", responseBody)
|
||||
} else if (contentType == ContentType.XML) {
|
||||
bb.addLine("def responseBody = new XmlSlurper().parseText($responseAsString)")
|
||||
// TODO xml validation
|
||||
}
|
||||
return toTrim
|
||||
}
|
||||
|
||||
private Object extractServerValueFromBody(bodyValue) {
|
||||
protected String getBodyAsString() {
|
||||
Object bodyValue = extractServerValueFromBody(request.body.serverValue)
|
||||
return trimRepeatedQuotes(new JsonOutput().toJson(bodyValue))
|
||||
}
|
||||
|
||||
protected String trimRepeatedQuotes(String toTrim) {
|
||||
return toTrim.startsWith('"') ? toTrim.replaceAll('"', '') : toTrim
|
||||
}
|
||||
|
||||
protected Object extractServerValueFromBody(bodyValue) {
|
||||
if (bodyValue instanceof GString) {
|
||||
bodyValue = extractValue(bodyValue, { DslProperty dslProperty -> dslProperty.serverValue })
|
||||
} else {
|
||||
@@ -113,88 +119,86 @@ class SpockMethodBodyBuilder {
|
||||
return bodyValue
|
||||
}
|
||||
|
||||
private String buildUrl(Request request) {
|
||||
if (request.url)
|
||||
return request.url.serverValue;
|
||||
if (request.urlPath)
|
||||
return buildUrlFromUrlPath(request.urlPath)
|
||||
throw new IllegalStateException("URL is not set!")
|
||||
}
|
||||
|
||||
private String buildUrlFromUrlPath(UrlPath urlPath) {
|
||||
String params = urlPath.queryParameters.parameters
|
||||
.findAll(this.&allowedQueryParameter)
|
||||
.inject([]) { result, param ->
|
||||
result << "${param.name}=${resolveParamValue(param).toString()}"
|
||||
}.join('&')
|
||||
return "$urlPath.serverValue?$params"
|
||||
}
|
||||
|
||||
private boolean allowedQueryParameter(QueryParameter param) {
|
||||
protected boolean allowedQueryParameter(QueryParameter param) {
|
||||
return allowedQueryParameter(param.serverValue)
|
||||
}
|
||||
|
||||
private boolean allowedQueryParameter(MatchingStrategy matchingStrategy) {
|
||||
protected boolean allowedQueryParameter(MatchingStrategy matchingStrategy) {
|
||||
return matchingStrategy.type != MatchingStrategy.Type.ABSENT
|
||||
}
|
||||
|
||||
private boolean allowedQueryParameter(Object o) {
|
||||
protected boolean allowedQueryParameter(Object o) {
|
||||
return true
|
||||
}
|
||||
|
||||
private String resolveParamValue(QueryParameter param) {
|
||||
resolveParamValue(param.serverValue)
|
||||
protected String resolveParamValue(QueryParameter param) {
|
||||
return resolveParamValue(param.serverValue)
|
||||
}
|
||||
|
||||
private String resolveParamValue(Object value) {
|
||||
value.toString()
|
||||
protected String resolveParamValue(Object value) {
|
||||
return value.toString()
|
||||
}
|
||||
|
||||
private String resolveParamValue(MatchingStrategy matchingStrategy) {
|
||||
matchingStrategy.serverValue.toString()
|
||||
protected String resolveParamValue(MatchingStrategy matchingStrategy) {
|
||||
return matchingStrategy.serverValue.toString()
|
||||
}
|
||||
|
||||
private void processBodyElement(BlockBuilder blockBuilder, String property, def value) {
|
||||
if (value instanceof String) {
|
||||
if (value.startsWith('$')) {
|
||||
value = value.substring(1).replaceAll('\\$value', "responseBody$property")
|
||||
blockBuilder.addLine(value)
|
||||
} else {
|
||||
blockBuilder.addLine("responseBody$property == \"${value}\"")
|
||||
}
|
||||
} else if (value instanceof Map) {
|
||||
processMapElement(value, blockBuilder, property)
|
||||
} else if (value instanceof Map.Entry) {
|
||||
processEntryElement(blockBuilder, property, value)
|
||||
} else if (value instanceof List) {
|
||||
processArrayElements(value, property, blockBuilder)
|
||||
} else if (value instanceof Pattern) {
|
||||
blockBuilder.addLine("responseBody$property ==~ java.util.regex.Pattern.compile('${value}')")
|
||||
} else if (value instanceof DslProperty) {
|
||||
processBodyElement(blockBuilder, property, value.serverValue)
|
||||
} else if (value instanceof ExecutionProperty) {
|
||||
ExecutionProperty exec = (ExecutionProperty) value
|
||||
blockBuilder.addLine("${exec.insertValue("responseBody$property")}")
|
||||
protected void processBodyElement(BlockBuilder blockBuilder, String property, Object value) {
|
||||
blockBuilder.addLine("responseBody$property == ${value}")
|
||||
}
|
||||
|
||||
protected void processBodyElement(BlockBuilder blockBuilder, String property, String value) {
|
||||
if (value.startsWith('$')) {
|
||||
value = value.substring(1).replaceAll('\\$value', "responseBody$property")
|
||||
blockBuilder.addLine(value)
|
||||
} else {
|
||||
blockBuilder.addLine("responseBody$property == ${value}")
|
||||
blockBuilder.addLine("responseBody$property == \"${value}\"")
|
||||
}
|
||||
}
|
||||
|
||||
private void processMapElement(def value, BlockBuilder blockBuilder, String property) {
|
||||
value.each { entry -> processEntryElement(blockBuilder, property, entry) }
|
||||
protected void processBodyElement(BlockBuilder blockBuilder, String property, Pattern pattern) {
|
||||
blockBuilder.addLine("responseBody$property ==~ java.util.regex.Pattern.compile('${pattern.pattern()}')")
|
||||
}
|
||||
|
||||
private def processEntryElement(BlockBuilder blockBuilder, String property, def entry) {
|
||||
return processBodyElement(blockBuilder, property + "." + entry.key, entry.value)
|
||||
protected void processBodyElement(BlockBuilder blockBuilder, String property, DslProperty dslProperty) {
|
||||
processBodyElement(blockBuilder, property, dslProperty.serverValue)
|
||||
}
|
||||
|
||||
private void processArrayElements(List responseBody, String property, BlockBuilder blockBuilder) {
|
||||
responseBody.eachWithIndex {
|
||||
listElement, listIndex ->
|
||||
listElement.each { entry ->
|
||||
String prop = "$property[$listIndex]" ?: ''
|
||||
processBodyElement(blockBuilder, prop, entry)
|
||||
}
|
||||
protected void processBodyElement(BlockBuilder blockBuilder, String property, ExecutionProperty exec) {
|
||||
blockBuilder.addLine("${exec.insertValue("responseBody$property")}")
|
||||
}
|
||||
|
||||
protected void processBodyElement(BlockBuilder blockBuilder, String property, Map.Entry entry) {
|
||||
processBodyElement(blockBuilder, property + "." + entry.key, entry.value)
|
||||
}
|
||||
|
||||
protected void processBodyElement(BlockBuilder blockBuilder, String property, Map map) {
|
||||
map.each {
|
||||
processBodyElement(blockBuilder, property, it)
|
||||
}
|
||||
}
|
||||
|
||||
protected void processBodyElement(BlockBuilder blockBuilder, String property, List list) {
|
||||
list.eachWithIndex { listElement, listIndex ->
|
||||
String prop = "$property[$listIndex]" ?: ''
|
||||
processBodyElement(blockBuilder, prop, listElement)
|
||||
}
|
||||
}
|
||||
|
||||
protected ContentType getRequestContentType() {
|
||||
ContentType contentType = recognizeContentTypeFromHeader(request.headers)
|
||||
if (contentType == ContentType.UNKNOWN) {
|
||||
contentType = recognizeContentTypeFromContent(request.body.serverValue)
|
||||
}
|
||||
return contentType
|
||||
}
|
||||
|
||||
protected ContentType getResponseContentType() {
|
||||
ContentType contentType = recognizeContentTypeFromHeader(response.headers)
|
||||
if (contentType == ContentType.UNKNOWN) {
|
||||
contentType = recognizeContentTypeFromContent(response.body.serverValue)
|
||||
}
|
||||
return contentType
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,5 +4,5 @@ package io.codearte.accurest.config
|
||||
* @author Jakub Kubrynski
|
||||
*/
|
||||
enum TestMode {
|
||||
MOCKMVC, EXPLICIT
|
||||
MOCKMVC, EXPLICIT, JAXRSCLIENT
|
||||
}
|
||||
@@ -1,5 +1,15 @@
|
||||
package io.codearte.accurest.util
|
||||
|
||||
enum ContentType {
|
||||
JSON, XML, UNKNOWN
|
||||
|
||||
JSON("application/json"),
|
||||
XML("application/xml"),
|
||||
UNKNOWN("application/octet-stream")
|
||||
|
||||
final String mimeType
|
||||
|
||||
ContentType(String mimeType) {
|
||||
this.mimeType = mimeType
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,362 @@
|
||||
package io.codearte.accurest.builder
|
||||
|
||||
import io.codearte.accurest.dsl.GroovyDsl
|
||||
import spock.lang.Issue
|
||||
import spock.lang.Specification
|
||||
|
||||
class JaxRsClientSpockMethodBuilderSpec extends Specification {
|
||||
|
||||
def "should generate assertions for simple response body"() {
|
||||
given:
|
||||
GroovyDsl contractDsl = GroovyDsl.make {
|
||||
request {
|
||||
method "GET"
|
||||
url "test"
|
||||
}
|
||||
response {
|
||||
status 200
|
||||
body """{
|
||||
"property1": "a",
|
||||
"property2": "b"
|
||||
}"""
|
||||
}
|
||||
}
|
||||
JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
when:
|
||||
builder.appendTo(blockBuilder)
|
||||
then:
|
||||
blockBuilder.toString().contains("responseBody.property1 == \"a\"")
|
||||
blockBuilder.toString().contains("responseBody.property2 == \"b\"")
|
||||
}
|
||||
|
||||
@Issue("#79")
|
||||
def "should generate assertions for simple response body constructed from map with a list"() {
|
||||
given:
|
||||
GroovyDsl contractDsl = GroovyDsl.make {
|
||||
request {
|
||||
method "GET"
|
||||
url "test"
|
||||
}
|
||||
response {
|
||||
status 200
|
||||
body(
|
||||
property1: 'a',
|
||||
property2: [
|
||||
[a: 'sth'],
|
||||
[b: 'sthElse']
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
when:
|
||||
builder.appendTo(blockBuilder)
|
||||
then:
|
||||
blockBuilder.toString().contains("responseBody.property1 == \"a\"")
|
||||
blockBuilder.toString().contains("responseBody.property2[0].a == \"sth\"")
|
||||
blockBuilder.toString().contains("responseBody.property2[1].b == \"sthElse\"")
|
||||
}
|
||||
|
||||
@Issue("#82")
|
||||
def "should generate proper request when body constructed from map with a list"() {
|
||||
given:
|
||||
GroovyDsl contractDsl = GroovyDsl.make {
|
||||
request {
|
||||
method "GET"
|
||||
url "test"
|
||||
body(
|
||||
items: ['HOP']
|
||||
)
|
||||
}
|
||||
response {
|
||||
status 200
|
||||
}
|
||||
}
|
||||
JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
when:
|
||||
builder.appendTo(blockBuilder)
|
||||
then:
|
||||
blockBuilder.toString().contains("entity('{\"items\":[\"HOP\"]}', 'application/json')")
|
||||
}
|
||||
|
||||
@Issue("#88")
|
||||
def "should generate proper request when body constructed from GString"() {
|
||||
given:
|
||||
GroovyDsl contractDsl = GroovyDsl.make {
|
||||
request {
|
||||
method "GET"
|
||||
url "test"
|
||||
body(
|
||||
"property1=VAL1"
|
||||
)
|
||||
}
|
||||
response {
|
||||
status 200
|
||||
}
|
||||
}
|
||||
JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
when:
|
||||
builder.appendTo(blockBuilder)
|
||||
then:
|
||||
blockBuilder.toString().contains("entity('property1=VAL1', 'application/octet-stream')")
|
||||
}
|
||||
|
||||
def "should generate assertions for array in response body"() {
|
||||
given:
|
||||
GroovyDsl contractDsl = GroovyDsl.make {
|
||||
request {
|
||||
method "GET"
|
||||
url "test"
|
||||
}
|
||||
response {
|
||||
status 200
|
||||
body """[
|
||||
{
|
||||
"property1": "a"
|
||||
},
|
||||
{
|
||||
"property2": "b"
|
||||
}]"""
|
||||
}
|
||||
}
|
||||
JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
when:
|
||||
builder.appendTo(blockBuilder)
|
||||
then:
|
||||
blockBuilder.toString().contains("responseBody[0].property1 == \"a\"")
|
||||
blockBuilder.toString().contains("responseBody[1].property2 == \"b\"")
|
||||
}
|
||||
|
||||
def "should generate assertions for array inside response body element"() {
|
||||
given:
|
||||
GroovyDsl contractDsl = GroovyDsl.make {
|
||||
request {
|
||||
method "GET"
|
||||
url "test"
|
||||
}
|
||||
response {
|
||||
status 200
|
||||
body """{
|
||||
"property1": [
|
||||
{ "property2": "test1"},
|
||||
{ "property3": "test2"}
|
||||
]
|
||||
}"""
|
||||
}
|
||||
}
|
||||
JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
when:
|
||||
builder.appendTo(blockBuilder)
|
||||
then:
|
||||
blockBuilder.toString().contains("responseBody.property1[0].property2 == \"test1\"")
|
||||
blockBuilder.toString().contains("responseBody.property1[1].property3 == \"test2\"")
|
||||
}
|
||||
|
||||
def "should generate assertions for nested objects in response body"() {
|
||||
given:
|
||||
GroovyDsl contractDsl = GroovyDsl.make {
|
||||
request {
|
||||
method "GET"
|
||||
url "test"
|
||||
}
|
||||
response {
|
||||
status 200
|
||||
body '''\
|
||||
{
|
||||
"property1": "a",
|
||||
"property2": {"property3": "b"}
|
||||
}
|
||||
'''
|
||||
}
|
||||
}
|
||||
JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
when:
|
||||
builder.appendTo(blockBuilder)
|
||||
then:
|
||||
blockBuilder.toString().contains("responseBody.property1 == \"a\"")
|
||||
blockBuilder.toString().contains("responseBody.property2.property3 == \"b\"")
|
||||
}
|
||||
|
||||
def "should generate regex assertions for map objects in response body"() {
|
||||
given:
|
||||
GroovyDsl contractDsl = GroovyDsl.make {
|
||||
request {
|
||||
method "GET"
|
||||
url "test"
|
||||
}
|
||||
response {
|
||||
status 200
|
||||
body(
|
||||
property1: "a",
|
||||
property2: value(
|
||||
client('123'),
|
||||
server(regex('[0-9]{3}'))
|
||||
)
|
||||
)
|
||||
headers {
|
||||
header('Content-Type': 'application/json')
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
when:
|
||||
builder.appendTo(blockBuilder)
|
||||
then:
|
||||
blockBuilder.toString().contains("responseBody.property1 == \"a\"")
|
||||
blockBuilder.toString().contains("responseBody.property2 ==~ java.util.regex.Pattern.compile('[0-9]{3}')")
|
||||
}
|
||||
|
||||
def "should generate regex assertions for string objects in response body"() {
|
||||
given:
|
||||
GroovyDsl contractDsl = GroovyDsl.make {
|
||||
request {
|
||||
method "GET"
|
||||
url "test"
|
||||
}
|
||||
response {
|
||||
status 200
|
||||
body("""{"property1":"a","property2":"${value(client('123'), server(regex('[0-9]{3}')))}"}""")
|
||||
headers {
|
||||
header('Content-Type': 'application/json')
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
when:
|
||||
builder.appendTo(blockBuilder)
|
||||
then:
|
||||
blockBuilder.toString().contains("responseBody.property1 == \"a\"")
|
||||
blockBuilder.toString().contains("responseBody.property2 ==~ java.util.regex.Pattern.compile('[0-9]{3}')")
|
||||
}
|
||||
|
||||
def "should ignore 'Accept' header and use 'request' method"() {
|
||||
given:
|
||||
GroovyDsl contractDsl = GroovyDsl.make {
|
||||
request {
|
||||
method "GET"
|
||||
url "test"
|
||||
headers {
|
||||
header("Accept", "text/plain")
|
||||
}
|
||||
}
|
||||
response {
|
||||
status 200
|
||||
}
|
||||
}
|
||||
JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
when:
|
||||
builder.appendTo(blockBuilder)
|
||||
then:
|
||||
blockBuilder.toString().contains("request('text/plain')")
|
||||
}
|
||||
|
||||
def "should ignore 'Content-Type' header and use 'entity' method"() {
|
||||
given:
|
||||
GroovyDsl contractDsl = GroovyDsl.make {
|
||||
request {
|
||||
method "GET"
|
||||
url "test"
|
||||
headers {
|
||||
header("Content-Type", "text/plain")
|
||||
header("Timer", "123")
|
||||
}
|
||||
body ''
|
||||
}
|
||||
response {
|
||||
status 200
|
||||
}
|
||||
}
|
||||
JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
when:
|
||||
builder.appendTo(blockBuilder)
|
||||
then:
|
||||
blockBuilder.toString().contains("entity('', 'text/plain')")
|
||||
blockBuilder.toString().contains("header('Timer', '123')")
|
||||
!blockBuilder.toString().contains("header('Content-Type'")
|
||||
|
||||
}
|
||||
|
||||
def "should generate a call with an url path and query parameters"() {
|
||||
given:
|
||||
GroovyDsl contractDsl = GroovyDsl.make {
|
||||
request {
|
||||
method 'GET'
|
||||
urlPath('/users') {
|
||||
queryParameters {
|
||||
parameter 'limit': $(client(equalTo("20")), server(equalTo("10")))
|
||||
parameter 'offset': $(client(containing("20")), server(equalTo("20")))
|
||||
parameter 'filter': "email"
|
||||
parameter 'sort': equalTo("name")
|
||||
parameter 'search': $(client(notMatching(~/^\/[0-9]{2}$/)), server("55"))
|
||||
parameter 'age': $(client(notMatching("^\\w*\$")), server("99"))
|
||||
parameter 'name': $(client(matching("Denis.*")), server("Denis.Stepanov"))
|
||||
parameter 'email': "bob@email.com"
|
||||
parameter 'hello': $(client(matching("Denis.*")), server(absent()))
|
||||
parameter 'hello': absent()
|
||||
}
|
||||
}
|
||||
}
|
||||
response {
|
||||
status 200
|
||||
body """
|
||||
{
|
||||
"property1": "a",
|
||||
"property2": "b"
|
||||
}
|
||||
"""
|
||||
}
|
||||
}
|
||||
JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
when:
|
||||
builder.appendTo(blockBuilder)
|
||||
def spockTest = blockBuilder.toString()
|
||||
then:
|
||||
spockTest.contains("queryParam('limit', '10'")
|
||||
spockTest.contains("queryParam('offset', '20'")
|
||||
spockTest.contains("queryParam('filter', 'email'")
|
||||
spockTest.contains("queryParam('sort', 'name'")
|
||||
spockTest.contains("queryParam('search', '55'")
|
||||
spockTest.contains("queryParam('age', '99'")
|
||||
spockTest.contains("queryParam('name', 'Denis.Stepanov'")
|
||||
spockTest.contains("queryParam('email', 'bob@email.com'")
|
||||
spockTest.contains('responseBody.property1 == "a"')
|
||||
spockTest.contains('responseBody.property2 == "b"')
|
||||
}
|
||||
|
||||
def "should generate test for empty body"() {
|
||||
given:
|
||||
GroovyDsl contractDsl = GroovyDsl.make {
|
||||
request {
|
||||
method('POST')
|
||||
url("/ws/payments")
|
||||
body("")
|
||||
}
|
||||
response {
|
||||
status 406
|
||||
}
|
||||
}
|
||||
JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
when:
|
||||
builder.appendTo(blockBuilder)
|
||||
def spockTest = blockBuilder.toString()
|
||||
then:
|
||||
spockTest.contains("entity('', 'application/octet-stream')")
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import spock.lang.Specification
|
||||
/**
|
||||
* @author Jakub Kubrynski
|
||||
*/
|
||||
class SpockMethodBuilderSpec extends Specification {
|
||||
class MockMvcSpockMethodBuilderSpec extends Specification {
|
||||
|
||||
def "should generate assertions for simple response body"() {
|
||||
given:
|
||||
@@ -24,7 +24,7 @@ class SpockMethodBuilderSpec extends Specification {
|
||||
}"""
|
||||
}
|
||||
}
|
||||
SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl)
|
||||
MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
when:
|
||||
builder.appendTo(blockBuilder)
|
||||
@@ -52,7 +52,7 @@ class SpockMethodBuilderSpec extends Specification {
|
||||
)
|
||||
}
|
||||
}
|
||||
SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl)
|
||||
MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
when:
|
||||
builder.appendTo(blockBuilder)
|
||||
@@ -77,7 +77,7 @@ class SpockMethodBuilderSpec extends Specification {
|
||||
status 200
|
||||
}
|
||||
}
|
||||
SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl)
|
||||
MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
when:
|
||||
builder.appendTo(blockBuilder)
|
||||
@@ -100,7 +100,7 @@ class SpockMethodBuilderSpec extends Specification {
|
||||
status 200
|
||||
}
|
||||
}
|
||||
SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl)
|
||||
MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
when:
|
||||
builder.appendTo(blockBuilder)
|
||||
@@ -126,7 +126,7 @@ class SpockMethodBuilderSpec extends Specification {
|
||||
}]"""
|
||||
}
|
||||
}
|
||||
SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl)
|
||||
MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
when:
|
||||
builder.appendTo(blockBuilder)
|
||||
@@ -152,7 +152,7 @@ class SpockMethodBuilderSpec extends Specification {
|
||||
}"""
|
||||
}
|
||||
}
|
||||
SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl)
|
||||
MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
when:
|
||||
builder.appendTo(blockBuilder)
|
||||
@@ -178,7 +178,7 @@ class SpockMethodBuilderSpec extends Specification {
|
||||
'''
|
||||
}
|
||||
}
|
||||
SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl)
|
||||
MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
when:
|
||||
builder.appendTo(blockBuilder)
|
||||
@@ -210,7 +210,7 @@ class SpockMethodBuilderSpec extends Specification {
|
||||
|
||||
}
|
||||
}
|
||||
SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl)
|
||||
MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
when:
|
||||
builder.appendTo(blockBuilder)
|
||||
@@ -236,7 +236,7 @@ class SpockMethodBuilderSpec extends Specification {
|
||||
|
||||
}
|
||||
}
|
||||
SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl)
|
||||
MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
when:
|
||||
builder.appendTo(blockBuilder)
|
||||
@@ -275,7 +275,7 @@ class SpockMethodBuilderSpec extends Specification {
|
||||
"""
|
||||
}
|
||||
}
|
||||
SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl)
|
||||
MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
when:
|
||||
builder.appendTo(blockBuilder)
|
||||
@@ -298,7 +298,7 @@ class SpockMethodBuilderSpec extends Specification {
|
||||
status 406
|
||||
}
|
||||
}
|
||||
SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl)
|
||||
MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
when:
|
||||
builder.appendTo(blockBuilder)
|
||||
@@ -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,95 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath "org.springframework.boot:spring-boot-gradle-plugin:1.2.1.RELEASE"
|
||||
}
|
||||
}
|
||||
|
||||
ext {
|
||||
spockVersion = '0.7-groovy-2.0'
|
||||
restAssuredVersion = '2.4.0'
|
||||
|
||||
accurestStubsBaseDirectory = 'src/test/resources/stubs'
|
||||
}
|
||||
|
||||
subprojects {
|
||||
apply plugin: 'groovy'
|
||||
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
mavenLocal()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testCompile "org.codehaus.groovy:groovy-all:2.3.7"
|
||||
testCompile "org.spockframework:spock-core:$spockVersion"
|
||||
testCompile("junit:junit:4.12")
|
||||
testCompile('com.github.tomakehurst:wiremock:1.52') {
|
||||
exclude group: 'org.mortbay.jetty', module: 'servlet-api'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configure([project(':fraudDetectionService'), project(':loanApplicationService')]) {
|
||||
apply plugin: 'spring-boot'
|
||||
apply plugin: 'accurest'
|
||||
|
||||
ext {
|
||||
wireMockStubsOutputDirRoot = file("${project.buildDir}/production/${project.name}-stubs/")
|
||||
wireMockStubsOutputDir = new File(wireMockStubsOutputDirRoot, 'mappings/')
|
||||
}
|
||||
|
||||
accurest {
|
||||
targetFramework = 'Spock'
|
||||
testMode = 'JaxRsClient'
|
||||
baseClassForTests = 'com.blogspot.toomuchcoding.MvcSpec'
|
||||
contractsDslDir = file("${project.projectDir.absolutePath}/mappings/")
|
||||
generatedTestSourcesDir = file("${project.buildDir}/generated-sources/")
|
||||
stubsOutputDir = wireMockStubsOutputDir
|
||||
}
|
||||
|
||||
jar {
|
||||
version = '0.0.1'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile "javax.ws.rs:javax.ws.rs-api:2.0.1"
|
||||
compile 'org.glassfish.jersey.containers:jersey-container-jetty-http:2.15'
|
||||
compile('org.springframework.boot:spring-boot-starter-jersey:1.2.5.RELEASE') {
|
||||
exclude module: "spring-boot-starter-tomcat"
|
||||
}
|
||||
compile 'org.springframework.boot:spring-boot-starter-jetty:1.2.5.RELEASE'
|
||||
|
||||
testRuntime "org.spockframework:spock-spring:$spockVersion"
|
||||
|
||||
compile 'org.glassfish.jersey.connectors:jersey-apache-connector:2.15'
|
||||
testCompile 'org.springframework:spring-test:4.1.7.RELEASE'
|
||||
}
|
||||
|
||||
task cleanup(type: Delete) {
|
||||
delete 'src/test/resources/mappings', 'src/test/resources/stubs'
|
||||
}
|
||||
|
||||
clean.dependsOn('cleanup')
|
||||
|
||||
}
|
||||
|
||||
configure(project(':fraudDetectionService')) {
|
||||
test.dependsOn('generateWireMockClientStubs')
|
||||
}
|
||||
|
||||
configure(project(':loanApplicationService')) {
|
||||
|
||||
task copyCollaboratorStubs(type: Copy) {
|
||||
File fraudBuildDir = project(':fraudDetectionService').buildDir
|
||||
from(new File(fraudBuildDir, "/production/${project(':fraudDetectionService').name}-stubs/"))
|
||||
into "src/test/resources/"
|
||||
}
|
||||
|
||||
generateAccurest.dependsOn('copyCollaboratorStubs')
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.blogspot.toomuchcoding.frauddetection.model;
|
||||
|
||||
public enum FraudCheckStatus {
|
||||
OK, FRAUD
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.blogspot.toomuchcoding.frauddetection.model;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class FraudServiceRequest {
|
||||
|
||||
private String clientPesel;
|
||||
|
||||
private BigDecimal loanAmount;
|
||||
|
||||
public FraudServiceRequest() {
|
||||
}
|
||||
|
||||
public FraudServiceRequest(LoanApplication loanApplication) {
|
||||
this.clientPesel = loanApplication.getClient().getPesel();
|
||||
this.loanAmount = loanApplication.getAmount();
|
||||
}
|
||||
|
||||
public String getClientPesel() {
|
||||
return clientPesel;
|
||||
}
|
||||
|
||||
public void setClientPesel(String clientPesel) {
|
||||
this.clientPesel = clientPesel;
|
||||
}
|
||||
|
||||
public BigDecimal getLoanAmount() {
|
||||
return loanAmount;
|
||||
}
|
||||
|
||||
public void setLoanAmount(BigDecimal loanAmount) {
|
||||
this.loanAmount = loanAmount;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.blogspot.toomuchcoding.frauddetection.model;
|
||||
|
||||
public class FraudServiceResponse {
|
||||
|
||||
private FraudCheckStatus fraudCheckStatus;
|
||||
|
||||
private String rejectionReason;
|
||||
|
||||
public FraudServiceResponse() {
|
||||
}
|
||||
|
||||
public FraudCheckStatus getFraudCheckStatus() {
|
||||
return fraudCheckStatus;
|
||||
}
|
||||
|
||||
public void setFraudCheckStatus(FraudCheckStatus fraudCheckStatus) {
|
||||
this.fraudCheckStatus = fraudCheckStatus;
|
||||
}
|
||||
|
||||
public String getRejectionReason() {
|
||||
return rejectionReason;
|
||||
}
|
||||
|
||||
public void setRejectionReason(String rejectionReason) {
|
||||
this.rejectionReason = rejectionReason;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.blogspot.toomuchcoding.frauddetection.model;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class LoanApplication {
|
||||
|
||||
private Client client;
|
||||
|
||||
private BigDecimal amount;
|
||||
|
||||
private String loanApplicationId;
|
||||
|
||||
public Client getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
public void setClient(Client client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
public void setAmount(BigDecimal amount) {
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public String getLoanApplicationId() {
|
||||
return loanApplicationId;
|
||||
}
|
||||
|
||||
public void setLoanApplicationId(String loanApplicationId) {
|
||||
this.loanApplicationId = loanApplicationId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.blogspot.toomuchcoding.frauddetection.model;
|
||||
|
||||
public class LoanApplicationResult {
|
||||
|
||||
private LoanApplicationStatus loanApplicationStatus;
|
||||
|
||||
private String rejectionReason;
|
||||
|
||||
public LoanApplicationResult() {
|
||||
}
|
||||
|
||||
public LoanApplicationResult(LoanApplicationStatus loanApplicationStatus, String rejectionReason) {
|
||||
this.loanApplicationStatus = loanApplicationStatus;
|
||||
this.rejectionReason = rejectionReason;
|
||||
}
|
||||
|
||||
public LoanApplicationStatus getLoanApplicationStatus() {
|
||||
return loanApplicationStatus;
|
||||
}
|
||||
|
||||
public void setLoanApplicationStatus(LoanApplicationStatus loanApplicationStatus) {
|
||||
this.loanApplicationStatus = loanApplicationStatus;
|
||||
}
|
||||
|
||||
public String getRejectionReason() {
|
||||
return rejectionReason;
|
||||
}
|
||||
|
||||
public void setRejectionReason(String rejectionReason) {
|
||||
this.rejectionReason = rejectionReason;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.blogspot.toomuchcoding.frauddetection.model;
|
||||
|
||||
public enum LoanApplicationStatus {
|
||||
LOAN_APPLIED, LOAN_APPLICATION_REJECTED
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
server.port=8090
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.blogspot.toomuchcoding
|
||||
|
||||
import com.blogspot.toomuchcoding.frauddetection.Application
|
||||
import com.blogspot.toomuchcoding.frauddetection.LoanApplicationService
|
||||
import com.blogspot.toomuchcoding.frauddetection.model.Client
|
||||
import com.blogspot.toomuchcoding.frauddetection.model.LoanApplication
|
||||
import com.blogspot.toomuchcoding.frauddetection.model.LoanApplicationResult
|
||||
import com.blogspot.toomuchcoding.frauddetection.model.LoanApplicationStatus
|
||||
import com.github.tomakehurst.wiremock.junit.WireMockClassRule
|
||||
import org.junit.ClassRule
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.test.SpringApplicationContextLoader
|
||||
import org.springframework.test.context.ContextConfiguration
|
||||
import spock.lang.Shared
|
||||
import spock.lang.Specification
|
||||
|
||||
@ContextConfiguration(loader = SpringApplicationContextLoader, classes = Application)
|
||||
class LoanApplicationServiceSpec extends Specification {
|
||||
|
||||
@ClassRule
|
||||
@Shared
|
||||
WireMockClassRule wireMockRule = new WireMockClassRule()
|
||||
|
||||
@Autowired
|
||||
LoanApplicationService sut
|
||||
|
||||
def 'should successfully apply for loan'() {
|
||||
given:
|
||||
LoanApplication application =
|
||||
new LoanApplication(client: new Client(pesel: '1234567890'), amount: 123.123)
|
||||
when:
|
||||
LoanApplicationResult loanApplication = sut.loanApplication(application)
|
||||
then:
|
||||
loanApplication.loanApplicationStatus == LoanApplicationStatus.LOAN_APPLIED
|
||||
loanApplication.rejectionReason == null
|
||||
}
|
||||
|
||||
def 'should be rejected due to abnormal loan amount'() {
|
||||
given:
|
||||
LoanApplication application =
|
||||
new LoanApplication(client: new Client(pesel: '1234567890'), amount: 99_999)
|
||||
when:
|
||||
LoanApplicationResult loanApplication = sut.loanApplication(application)
|
||||
then:
|
||||
loanApplication.loanApplicationStatus == LoanApplicationStatus.LOAN_APPLICATION_REJECTED
|
||||
loanApplication.rejectionReason == 'Amount too high'
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"request": {
|
||||
"method": "PUT",
|
||||
"headers": {
|
||||
"Content-Type": {
|
||||
"equalTo": "application/vnd.fraud.v1+json"
|
||||
}
|
||||
},
|
||||
"url": "/fraudcheck",
|
||||
"bodyPatterns": [
|
||||
{
|
||||
"matches": "\\s*\\{\\s*\"clientPesel\"\\s*:\\s*\"?[0-9]{10}\"?\\s*,\\s*\"loanAmount\"\\s*:\\s*\"?99999\"?\\s*\\}\\s*"
|
||||
}
|
||||
]
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"Content-Type": "application/vnd.fraud.v1+json"
|
||||
},
|
||||
"body": "{\"fraudCheckStatus\":\"FRAUD\",\"rejectionReason\":\"Amount too high\"}"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"request": {
|
||||
"method": "PUT",
|
||||
"headers": {
|
||||
"Content-Type": {
|
||||
"equalTo": "application/vnd.fraud.v1+json"
|
||||
}
|
||||
},
|
||||
"url": "/fraudcheck",
|
||||
"bodyPatterns": [
|
||||
{
|
||||
"matches": "\\s*\\{\\s*\"clientPesel\"\\s*:\\s*\"?[0-9]{10}\"?\\s*,\\s*\"loanAmount\"\\s*:\\s*\"?123.123\"?\\s*\\}\\s*"
|
||||
}
|
||||
]
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"Content-Type": "application/vnd.fraud.v1+json"
|
||||
},
|
||||
"body": "{\"fraudCheckStatus\":\"OK\",\"rejectionReason\":null}"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
include ':fraudDetectionService'
|
||||
include ':loanApplicationService'
|
||||
Reference in New Issue
Block a user