diff --git a/.gitignore b/.gitignore index bd4b5b192b..05f150069a 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ target/ build/ hs_err_pid* -.DS_Store \ No newline at end of file +.DS_Store +*.log \ No newline at end of file diff --git a/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/RecursiveFilesConverter.groovy b/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/RecursiveFilesConverter.groovy index d073a7af2e..867aab8a34 100644 --- a/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/RecursiveFilesConverter.groovy +++ b/accurest-converters/src/main/groovy/io/codearte/accurest/wiremock/RecursiveFilesConverter.groovy @@ -35,11 +35,14 @@ class RecursiveFilesConverter { return } String convertedContent = singleFileConverter.convertContent(entry.key.last().toString(), contract) + if (!convertedContent) { + return + } Path absoluteTargetPath = createAndReturnTargetDirectory(sourceFile) File newGroovyFile = createTargetFileWithProperName(absoluteTargetPath, sourceFile) newGroovyFile.setText(convertedContent, StandardCharsets.UTF_8.toString()) } catch (Exception e) { - throw new ConversionAccurestException("Unable to make convertion of ${sourceFile.name}", e) + throw new ConversionAccurestException("Unable to make conversion of ${sourceFile.name}", e) } } } @@ -49,12 +52,12 @@ class RecursiveFilesConverter { Path relativePath = Paths.get(properties.contractsDslDir.toURI()).relativize(sourceFile.parentFile.toPath()) Path absoluteTargetPath = properties.stubsOutputDir.toPath().resolve(relativePath) Files.createDirectories(absoluteTargetPath) - absoluteTargetPath + return absoluteTargetPath } private File createTargetFileWithProperName(Path absoluteTargetPath, File sourceFile) { File newGroovyFile = new File(absoluteTargetPath.toFile(), singleFileConverter.generateOutputFileNameForInput(sourceFile.name)) log.info("Creating new json [$newGroovyFile.path]") - newGroovyFile + return newGroovyFile } } diff --git a/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/RecursiveFilesConverterSpec.groovy b/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/RecursiveFilesConverterSpec.groovy index fc0a0014b6..754445866e 100755 --- a/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/RecursiveFilesConverterSpec.groovy +++ b/accurest-converters/src/test/groovy/io/codearte/accurest/wiremock/RecursiveFilesConverterSpec.groovy @@ -28,7 +28,7 @@ class RecursiveFilesConverterSpec extends Specification { and: def singleFileConverterStub = Stub(SingleFileConverter) singleFileConverterStub.canHandleFileName(_) >> { String fileName -> fileName.endsWith(".groovy") } - singleFileConverterStub.convertContent(_) >> { "converted" } + singleFileConverterStub.convertContent(_, _) >> { "converted" } singleFileConverterStub.generateOutputFileNameForInput(_) >> { String inputFileName -> inputFileName.replaceAll('.groovy', '.json') } RecursiveFilesConverter recursiveFilesConverter = new RecursiveFilesConverter(singleFileConverterStub, properties) @@ -53,7 +53,7 @@ class RecursiveFilesConverterSpec extends Specification { and: def singleFileConverterStub = Stub(SingleFileConverter) singleFileConverterStub.canHandleFileName(_) >> { String fileName -> fileName.endsWith(".groovy") } - singleFileConverterStub.convertContent(_) >> { "converted" } + singleFileConverterStub.convertContent(_, _) >> { "converted" } singleFileConverterStub.generateOutputFileNameForInput(_) >> { String inputFileName -> inputFileName.replaceAll('.groovy', '.json') } RecursiveFilesConverter recursiveFilesConverter = new RecursiveFilesConverter(singleFileConverterStub, properties) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/SingleTestGenerator.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/SingleTestGenerator.groovy index 58faa81302..f2ff1a7617 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/SingleTestGenerator.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/SingleTestGenerator.groovy @@ -1,12 +1,15 @@ package io.codearte.accurest +import groovy.transform.Canonical import groovy.transform.PackageScope import groovy.util.logging.Slf4j import io.codearte.accurest.builder.ClassBuilder 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.file.Contract +import org.codehaus.groovy.control.CompilerConfiguration import static io.codearte.accurest.builder.ClassBuilder.createClass import static io.codearte.accurest.builder.MethodBuilder.createTestMethod @@ -49,50 +52,88 @@ class SingleTestGenerator { clazz.addClassLevelAnnotation(configProperties.targetFramework.getOrderAnnotation()) } - if (configProperties.testMode == TestMode.JAXRSCLIENT) { - clazz.addStaticImport('javax.ws.rs.client.Entity.*') - if (configProperties.targetFramework == TestFramework.JUNIT) { - clazz.addImport('javax.ws.rs.core.Response') - } - } else if (configProperties.testMode == TestMode.MOCKMVC) { - clazz.addStaticImport('com.jayway.restassured.module.mockmvc.RestAssuredMockMvc.*') - } else { - clazz.addStaticImport('com.jayway.restassured.RestAssured.*') - } - - if (configProperties.targetFramework == TestFramework.JUNIT) { - if (configProperties.testMode == TestMode.MOCKMVC) { - clazz.addImport('com.jayway.restassured.module.mockmvc.specification.MockMvcRequestSpecification') - clazz.addImport('com.jayway.restassured.response.ResponseOptions') - } - clazz.addImport('org.junit.Test') - clazz.addStaticImport('org.assertj.core.api.Assertions.assertThat') - } - - if (configProperties.ruleClassForTests) { - clazz.addImport('org.junit.Rule').addRule(configProperties.ruleClassForTests) - } - addJsonPathRelatedImports(clazz) - listOfFiles.each { - clazz.addMethod(createTestMethod(it, configProperties)) + Map contracts = listOfFiles.collectEntries { + File stubsFile = it.path.toFile() + log.debug("Stub content from file [${stubsFile.text}]") + GroovyDsl stubContent = new GroovyShell(delegate.class.classLoader, new Binding(), new CompilerConfiguration(sourceEncoding:'UTF-8')).evaluate(stubsFile) + TestType testType = (stubContent.inputMessage || stubContent.outputMessage) ? TestType.MESSAGING : TestType.HTTP + return [(new ParsedDsl(it, stubContent, stubsFile)) : testType] + } + + boolean conditionalImportsAdded = false + contracts.each { ParsedDsl key, TestType value -> + if (!conditionalImportsAdded) { + if (value == TestType.HTTP) { + if (configProperties.testMode == TestMode.JAXRSCLIENT) { + clazz.addStaticImport('javax.ws.rs.client.Entity.*') + if (configProperties.targetFramework == TestFramework.JUNIT) { + clazz.addImport('javax.ws.rs.core.Response') + } + } else if (configProperties.testMode == TestMode.MOCKMVC) { + clazz.addStaticImport('com.jayway.restassured.module.mockmvc.RestAssuredMockMvc.*') + } else { + clazz.addStaticImport('com.jayway.restassured.RestAssured.*') + } + } + if (configProperties.targetFramework == TestFramework.JUNIT) { + if (value == TestType.HTTP && configProperties.testMode == TestMode.MOCKMVC) { + clazz.addImport('com.jayway.restassured.module.mockmvc.specification.MockMvcRequestSpecification') + clazz.addImport('com.jayway.restassured.response.ResponseOptions') + } + clazz.addImport('org.junit.Test') + clazz.addStaticImport('org.assertj.core.api.Assertions.assertThat') + } + if (configProperties.ruleClassForTests) { + clazz.addImport('org.junit.Rule').addRule(configProperties.ruleClassForTests) + } + if (value == TestType.MESSAGING) { + addMessagingRelatedEntries(clazz) + } + conditionalImportsAdded = true + } + clazz.addMethod(createTestMethod(key.contract, key.stubsFile, key.groovyDsl, configProperties)) } return clazz.build() } + @Canonical + private static class ParsedDsl { + Contract contract + GroovyDsl groovyDsl + File stubsFile + } + + private static enum TestType { + MESSAGING, HTTP + } + private boolean isScenarioClass(Collection listOfFiles) { listOfFiles.find({ it.order != null }) != null } private ClassBuilder addJsonPathRelatedImports(ClassBuilder clazz) { clazz.addImport(['com.jayway.jsonpath.DocumentContext', - 'com.jayway.jsonpath.JsonPath']) + 'com.jayway.jsonpath.JsonPath', + ]) if (jsonAssertPresent()) { clazz.addStaticImport(JSON_ASSERT_STATIC_IMPORT) } } + private ClassBuilder addMessagingRelatedEntries(ClassBuilder clazz) { + clazz.addField(['@Inject AccurestMessaging accurestMessaging', + 'ObjectMapper accurestObjectMapper = new ObjectMapper()' + ]) + clazz.addImport([ 'javax.inject.Inject', + 'com.fasterxml.jackson.databind.ObjectMapper', + 'io.codearte.accurest.messaging.AccurestMessage', + 'io.codearte.accurest.messaging.AccurestMessaging', + ]) + clazz.addStaticImport('io.codearte.accurest.messaging.AccurestMessagingUtil.headers') + } + private static boolean jsonAssertPresent() { try { Class.forName(JSON_ASSERT_CLASS) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/ClassBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/ClassBuilder.groovy index 8975faa18f..459bf57a4c 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/builder/ClassBuilder.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/ClassBuilder.groovy @@ -3,7 +3,6 @@ package io.codearte.accurest.builder import io.codearte.accurest.config.AccurestConfigProperties import io.codearte.accurest.config.TestFramework import io.codearte.accurest.util.NamesUtil - /** * @author Jakub Kubrynski */ @@ -15,6 +14,7 @@ class ClassBuilder { private final List imports = [] private final List staticImports = [] private final List rules = [] + private final List fields = [] private final List methods = [] private final List classLevelAnnotations = [] private final TestFramework lang @@ -49,6 +49,7 @@ class ClassBuilder { return this } + ClassBuilder addStaticImport(String importToAdd) { staticImports << importToAdd return this @@ -59,6 +60,23 @@ class ClassBuilder { return this } + ClassBuilder addField(String fieldToAdd) { + fields << appendColonIfJUniTest(fieldToAdd) + return this + } + + private String appendColonIfJUniTest(String field) { + if (lang == TestFramework.JUNIT && !field.endsWith(';')) { + return "$field;" + } + return field + } + + ClassBuilder addField(List fieldsToAdd) { + fields.addAll(fieldsToAdd.collect { appendColonIfJUniTest(it) }) + return this + } + ClassBuilder addRule(String ruleClass) { imports << ruleClass rules << NamesUtil.afterLastDot(ruleClass) @@ -105,6 +123,13 @@ class ClassBuilder { clazz.addEmptyLine() } + fields.sort().each { + clazz.addLine(it) + } + if (!fields.empty) { + clazz.addEmptyLine() + } + methods.each { clazz.addBlock(it) } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/JUnitMessagingMethodBodyBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/JUnitMessagingMethodBodyBuilder.groovy new file mode 100644 index 0000000000..11228c64e2 --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/JUnitMessagingMethodBodyBuilder.groovy @@ -0,0 +1,166 @@ +package io.codearte.accurest.builder + +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 io.codearte.accurest.dsl.internal.Input +import io.codearte.accurest.dsl.internal.NamedProperty + +import java.util.regex.Pattern + +import static io.codearte.accurest.config.TestFramework.JUNIT +/** + * @author Jakub Kubrynski + */ +@PackageScope +@TypeChecked +class JUnitMessagingMethodBodyBuilder extends MessagingMethodBodyBuilder { + + JUnitMessagingMethodBodyBuilder(GroovyDsl stubDefinition) { + super(stubDefinition) + } + + @Override + protected String getInputString(Input request) { + if (request.triggeredBy) { + return request.triggeredBy.executionCommand + } + return "accurestMessaging.send(inputMessage, \"${request.messageFrom}\")" + } + + @Override + protected String getResponseBodyPropertyComparisonString(String property, String value) { + return "responseBody$property == \"${value}\"" + } + + @Override + protected void processBodyElement(BlockBuilder blockBuilder, String property, ExecutionProperty exec) { + blockBuilder.addLine("${exec.insertValue("parsedJson.read('\\\$$property')")}") + } + + @Override + protected void processBodyElement(BlockBuilder blockBuilder, String property, Map.Entry entry) { + processBodyElement(blockBuilder, property + "." + entry.key, entry.value) + } + + @Override + protected void processHeaderElement(BlockBuilder blockBuilder, String property, String value) { + blockBuilder.addLine("assertThat(response.getHeader(\"$property\")).${createHeaderComparison(value)}") + } + + @Override + protected void processHeaderElement(BlockBuilder blockBuilder, String property, Pattern pattern) { + blockBuilder.addLine("assertThat(response.getHeader(\"$property\")).${createHeaderComparison(pattern)}") + } + + @Override + protected void processHeaderElement(BlockBuilder blockBuilder, String property, ExecutionProperty exec) { + blockBuilder.addLine("${exec.insertValue("response.getHeader(\"$property\")")};") + } + + @Override + protected void validateResponseCodeBlock(BlockBuilder bb) { + + } + + @Override + protected void validateResponseHeadersBlock(BlockBuilder bb) { + bb.addLine("""AccurestMessage response = accurestMessaging.receiveMessage("${outputMessage.sentTo}");""") + outputMessage.headers?.collect { Header header ->\ + processHeaderElement(bb, header.name, header.serverValue) + } + } + + @Override + protected String getResponseAsString() { + return 'accurestObjectMapper.writeValueAsString(response.getPayload())' + } + + @Override + protected String addCommentSignIfRequired(String baseString) { + return "// $baseString" + } + + @Override + protected BlockBuilder addColonIfRequired(BlockBuilder blockBuilder) { + blockBuilder.addAtTheEnd(JUNIT.lineSuffix) + return blockBuilder + } + + @Override + protected String getPropertyInListString(String property, Integer listIndex) { + return "$property[$listIndex]" ?: '' + } + + @Override + protected String convertUnicodeEscapesIfRequired(String json) { + return StringEscapeUtils.unescapeJavaScript(json) + } + + @Override + protected String getParsedXmlResponseBodyString(String responseString) { + return "Object responseBody = new XmlSlurper().parseText($responseString);" + } + + @Override + protected String getSimpleResponseBodyString(String responseString) { + return "Object responseBody = ($responseString);" + } + + @Override + protected String getInputString() { + String request = 'AccurestMessage inputMessage = accurestMessaging.create(' + if (inputMessage.messageBody) { + request = "${request}\n\t\t\t\"${StringEscapeUtils.escapeJava(bodyAsString)}\"\n\t\t" + } + if (inputMessage.messageHeaders) { + request = "${request}, headers()\n" + } + inputMessage.messageHeaders?.collect { Header header -> + request = "${request}\t\t\t${getHeaderString(header)}" + } + return "${request})" + } + + @Override + protected String getHeaderString(Header header) { + return ".header(\"${getTestSideValue(header.name)}\", \"${getTestSideValue(header.serverValue)}\")" + } + + @Override + protected String getBodyString(String bodyAsString) { + return '' + } + + @Override + protected String getMultipartFileParameterContent(String propertyName, NamedProperty propertyValue) { + return "" + } + + @Override + protected String getParameterString(Map.Entry parameter) { + return "" + } + + protected String convertHeaderComparison(String headerValue) { + return " == '$headerValue'" + } + + protected String convertHeaderComparison(Pattern headerValue) { + return "==~ java.util.regex.Pattern.compile('$headerValue')" + } + + protected String createHeaderComparison(Object headerValue) { + String escapedHeader = convertUnicodeEscapesIfRequired("$headerValue") + return "isEqualTo(\"$escapedHeader\");" + } + + protected String createHeaderComparison(Pattern headerValue) { + String escapedHeader = convertUnicodeEscapesIfRequired("$headerValue") + return "matches(\"$escapedHeader\");" + } + +} diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/JUnitMethodBodyBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/JUnitMethodBodyBuilder.groovy index 246a9b372a..02f8ad854f 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/builder/JUnitMethodBodyBuilder.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/JUnitMethodBodyBuilder.groovy @@ -21,7 +21,7 @@ import static io.codearte.accurest.util.ContentUtils.getJavaMultipartFileParamet */ @TypeChecked @PackageScope -abstract class JUnitMethodBodyBuilder extends MethodBodyBuilder { +abstract class JUnitMethodBodyBuilder extends RequestProcessingMethodBodyBuilder { JUnitMethodBodyBuilder(GroovyDsl stubDefinition) { super(stubDefinition) @@ -87,12 +87,12 @@ abstract class JUnitMethodBodyBuilder extends MethodBodyBuilder { } @Override - protected String getResponseString(Request request) { + protected String getInputString(Request request) { return 'ResponseOptions response = given().spec(request)' } @Override - protected String getRequestString() { + protected String getInputString() { return 'MockMvcRequestSpecification request = given()' } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodBodyBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodRequestProcessingBodyBuilder.groovy similarity index 95% rename from accurest-core/src/main/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodBodyBuilder.groovy rename to accurest-core/src/main/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodRequestProcessingBodyBuilder.groovy index cc47fc751a..86fe435034 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodBodyBuilder.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/JaxRsClientSpockMethodRequestProcessingBodyBuilder.groovy @@ -12,9 +12,9 @@ import java.util.regex.Pattern @PackageScope @TypeChecked -class JaxRsClientSpockMethodBodyBuilder extends SpockMethodBodyBuilder { +class JaxRsClientSpockMethodRequestProcessingBodyBuilder extends SpockMethodRequestProcessingBodyBuilder { - JaxRsClientSpockMethodBodyBuilder(GroovyDsl stubDefinition) { + JaxRsClientSpockMethodRequestProcessingBodyBuilder(GroovyDsl stubDefinition) { super(stubDefinition) } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/MessagingMethodBodyBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/MessagingMethodBodyBuilder.groovy new file mode 100644 index 0000000000..e5a523e894 --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/MessagingMethodBodyBuilder.groovy @@ -0,0 +1,87 @@ +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.Input +import io.codearte.accurest.dsl.internal.OutputMessage +import io.codearte.accurest.util.ContentType + +import static io.codearte.accurest.util.ContentUtils.recognizeContentTypeFromContent +import static io.codearte.accurest.util.ContentUtils.recognizeContentTypeFromHeader +/** + * @author Olga Maciaszek-Sharma + * @since 2016-02-17 + */ +@TypeChecked +@PackageScope +abstract class MessagingMethodBodyBuilder extends MethodBodyBuilder { + + protected final Input inputMessage + protected final OutputMessage outputMessage + + MessagingMethodBodyBuilder(GroovyDsl stubDefinition) { + this.inputMessage = stubDefinition.inputMessage + this.outputMessage = stubDefinition.outputMessage + } + + protected abstract String getInputString(Input request) + + @Override + protected boolean hasGivenSection() { + return !inputMessage.triggeredBy + } + + protected void processInput(BlockBuilder bb) { + + } + + protected void when(BlockBuilder bb) { + bb.addLine(getInputString(inputMessage)) + bb.indent() + + addColonIfRequired(bb) + bb.unindent() + } + + protected void then(BlockBuilder bb) { + validateResponseCodeBlock(bb) + if (inputMessage.assertThat) { + bb.addLine(inputMessage.assertThat.executionCommand) + addColonIfRequired(bb) + } + if (outputMessage) { + if (outputMessage.headers) { + validateResponseHeadersBlock(bb) + } + if (outputMessage.body) { + bb.endBlock() + if (outputMessage.headers) { + bb.addLine(addCommentSignIfRequired('and:')).startBlock() + } + validateResponseBodyBlock(bb, outputMessage.body.serverValue) + } + if (outputMessage.assertThat) { + bb.addLine(outputMessage.assertThat.executionCommand) + addColonIfRequired(bb) + } + } + } + + protected ContentType getResponseContentType() { + ContentType contentType = recognizeContentTypeFromHeader(outputMessage.headers) + if (contentType == ContentType.UNKNOWN) { + contentType = recognizeContentTypeFromContent(outputMessage.body.serverValue) + } + return contentType + } + + protected String getBodyAsString() { + Object bodyValue = extractServerValueFromBody(inputMessage.messageBody.serverValue) + String json = new JsonOutput().toJson(bodyValue) + json = convertUnicodeEscapesIfRequired(json) + return trimRepeatedQuotes(json) + } + +} diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/MethodBodyBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/MethodBodyBuilder.groovy index 9b93acc5f2..8ca6aade46 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/builder/MethodBodyBuilder.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/MethodBodyBuilder.groovy @@ -1,19 +1,8 @@ package io.codearte.accurest.builder -import groovy.json.JsonOutput import groovy.transform.PackageScope import groovy.transform.TypeChecked -import groovy.transform.TypeCheckingMode -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.NamedProperty -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.Url +import io.codearte.accurest.dsl.internal.* import io.codearte.accurest.util.ContentType import io.codearte.accurest.util.JsonPaths import io.codearte.accurest.util.JsonToJsonPathsConverter @@ -22,9 +11,6 @@ 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.ContentUtils.recognizeContentTypeFromContent -import static io.codearte.accurest.util.ContentUtils.recognizeContentTypeFromHeader - /** * @author Olga Maciaszek-Sharma * @since 2016-02-17 @@ -33,14 +19,6 @@ import static io.codearte.accurest.util.ContentUtils.recognizeContentTypeFromHea @PackageScope abstract class MethodBodyBuilder { - protected final Request request - protected final Response response - - MethodBodyBuilder(GroovyDsl stubDefinition) { - this.request = stubDefinition.request - this.response = stubDefinition.response - } - protected abstract void validateResponseCodeBlock(BlockBuilder bb) protected abstract void validateResponseHeadersBlock(BlockBuilder bb) @@ -71,9 +49,7 @@ abstract class MethodBodyBuilder { protected abstract String getSimpleResponseBodyString(String responseString) - protected abstract String getResponseString(Request request) - - protected abstract String getRequestString() + protected abstract String getInputString() protected abstract String getHeaderString(Header header) @@ -83,30 +59,30 @@ abstract class MethodBodyBuilder { protected abstract String getParameterString(Map.Entry parameter) + protected abstract void processInput(BlockBuilder bb) + + protected abstract void when(BlockBuilder bb) + + protected abstract void then(BlockBuilder bb) + + protected abstract ContentType getResponseContentType() + + protected abstract String getBodyAsString() + + protected abstract boolean hasGivenSection() + void appendTo(BlockBuilder blockBuilder) { blockBuilder.startBlock() - givenBlock(blockBuilder) + if (hasGivenSection()) { + givenBlock(blockBuilder) + } whenBlock(blockBuilder) thenBlock(blockBuilder) blockBuilder.endBlock() } - protected void thenBlock(BlockBuilder bb) { - bb.addLine(addCommentSignIfRequired('then:')) - bb.startBlock() - then(bb) - bb.endBlock() - } - - protected void whenBlock(BlockBuilder bb) { - bb.addLine(addCommentSignIfRequired('when:')) - bb.startBlock() - when(bb) - bb.endBlock().addEmptyLine() - } - protected void givenBlock(BlockBuilder bb) { bb.addLine(addCommentSignIfRequired('given:')) bb.startBlock() @@ -114,48 +90,29 @@ abstract class MethodBodyBuilder { bb.endBlock().addEmptyLine() } + protected void whenBlock(BlockBuilder bb) { + bb.addLine(addCommentSignIfRequired('when:')) + bb.startBlock() + when(bb) + bb.endBlock().addEmptyLine() + } + + protected void thenBlock(BlockBuilder bb) { + bb.addLine(addCommentSignIfRequired('then:')) + bb.startBlock() + then(bb) + bb.endBlock() + } + protected void given(BlockBuilder bb) { - bb.addLine(getRequestString()) + bb.addLine(getInputString()) bb.indent() - request.headers?.collect { Header header -> - bb.addLine(getHeaderString(header)) - } - if (request.body) { - bb.addLine(getBodyString(bodyAsString)) - } - if (request.multipart) { - multipartParameters?.each { Map.Entry entry -> bb.addLine(getMultipartParameterLine(entry)) } - } + processInput(bb) addColonIfRequired(bb) bb.unindent() } - protected void when(BlockBuilder bb) { - bb.addLine(getResponseString(request)) - bb.indent() - - String url = buildUrl(request) - String method = request.method.serverValue.toString().toLowerCase() - - bb.addLine(/.${method}("$url")/) - addColonIfRequired(bb) - bb.unindent() - } - - protected void then(BlockBuilder bb) { - validateResponseCodeBlock(bb) - if (response.headers) { - validateResponseHeadersBlock(bb) - } - if (response.body) { - bb.endBlock() - bb.addLine(addCommentSignIfRequired('and:')).startBlock() - validateResponseBodyBlock(bb) - } - } - - private void validateResponseBodyBlock(BlockBuilder bb) { - def responseBody = response.body.serverValue + protected void validateResponseBodyBlock(BlockBuilder bb, Object responseBody) { ContentType contentType = getResponseContentType() if (responseBody instanceof GString) { responseBody = extractValue(responseBody, contentType, { DslProperty dslProperty -> dslProperty.serverValue }) @@ -179,14 +136,6 @@ abstract class MethodBodyBuilder { } } - private ContentType getResponseContentType() { - ContentType contentType = recognizeContentTypeFromHeader(response.headers) - if (contentType == ContentType.UNKNOWN) { - contentType = recognizeContentTypeFromContent(response.body.serverValue) - } - return contentType - } - protected void appendJsonPath(BlockBuilder blockBuilder, String json) { blockBuilder.addLine(("DocumentContext parsedJson = JsonPath.parse($json)")) addColonIfRequired(blockBuilder) @@ -212,17 +161,6 @@ abstract class MethodBodyBuilder { protected void processBodyElement(BlockBuilder blockBuilder, String property, Object value) { } - protected String getBodyAsString() { - Object bodyValue = extractServerValueFromBody(request.body.serverValue) - String json = new JsonOutput().toJson(bodyValue) - json = convertUnicodeEscapesIfRequired(json) - return trimRepeatedQuotes(json) - } - - protected Map getMultipartParameters() { - return (Map) request?.multipart?.serverValue - } - protected String trimRepeatedQuotes(String toTrim) { return toTrim.startsWith('"') ? toTrim.replaceAll('"', '') : toTrim } @@ -236,18 +174,6 @@ abstract class MethodBodyBuilder { 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) } @@ -260,14 +186,6 @@ abstract class MethodBodyBuilder { return matchingStrategy.serverValue.toString() } - protected ContentType getRequestContentType() { - ContentType contentType = recognizeContentTypeFromHeader(request.headers) - if (contentType == ContentType.UNKNOWN) { - contentType = recognizeContentTypeFromContent(request.body.serverValue) - } - return contentType - } - protected String getTestSideValue(Object object) { return MapConverter.getTestSideValues(object).toString() } @@ -285,38 +203,5 @@ abstract class MethodBodyBuilder { } } - protected String buildUrl(Request request) { - if (request.url) - return getTestSideValue(buildUrlFromUrlPath(request.url)) - if (request.urlPath) - return getTestSideValue(buildUrlFromUrlPath(request.urlPath)) - throw new IllegalStateException("URL is not set!") - } - - @TypeChecked(TypeCheckingMode.SKIP) - protected String buildUrlFromUrlPath(Url url) { - if (hasQueryParams(url)) { - String params = url.queryParameters.parameters - .findAll(this.&allowedQueryParameter) - .inject([] as List) { List result, QueryParameter param -> - result << "${param.name}=${resolveParamValue(param).toString()}" - } - .join('&') - return "${MapConverter.getTestSideValues(url.serverValue)}?$params" - } - return MapConverter.getTestSideValues(url.serverValue) - } - - protected String getMultipartParameterLine(Map.Entry parameter) { - if (parameter.value instanceof NamedProperty) { - return ".multiPart(${getMultipartFileParameterContent(parameter.key, (NamedProperty) parameter.value)})" - } - return getParameterString(parameter) - } - - - private boolean hasQueryParams(Url url) { - return url.queryParameters - } } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/MethodBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/MethodBuilder.groovy index b3d9c5c286..b53e72db3e 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/builder/MethodBuilder.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/MethodBuilder.groovy @@ -7,8 +7,6 @@ import io.codearte.accurest.config.TestMode import io.codearte.accurest.dsl.GroovyDsl import io.codearte.accurest.file.Contract import io.codearte.accurest.util.NamesUtil -import org.codehaus.groovy.control.CompilerConfiguration - /** * @author Jakub Kubrynski */ @@ -27,10 +25,7 @@ class MethodBuilder { this.configProperties = configProperties } - static MethodBuilder createTestMethod(Contract contract, AccurestConfigProperties configProperties) { - File stubsFile = contract.path.toFile() - log.debug("Stub content from file [${stubsFile.text}]") - GroovyDsl stubContent = new GroovyShell(this.classLoader, new Binding(), new CompilerConfiguration(sourceEncoding:'UTF-8')).evaluate(stubsFile) + static MethodBuilder createTestMethod(Contract contract, File stubsFile, GroovyDsl stubContent, AccurestConfigProperties configProperties) { log.debug("Stub content Groovy DSL [$stubContent]") String methodName = NamesUtil.camelCase(NamesUtil.toLastDot(NamesUtil.afterLast(stubsFile.path, File.separator))) return new MethodBuilder(methodName, stubContent, configProperties, contract.ignored) @@ -43,12 +38,18 @@ class MethodBuilder { if (ignored) { blockBuilder.addLine('@Ignore') } - blockBuilder.addLine(configProperties.targetFramework.methodModifier + "validate_$methodName() {") + blockBuilder.addLine(configProperties.targetFramework.methodModifier + "validate_$methodName() throws Exception {") getMethodBodyBuilder().appendTo(blockBuilder) blockBuilder.addLine('}') } private MethodBodyBuilder getMethodBodyBuilder() { + if (stubContent.inputMessage || stubContent.outputMessage) { + if (configProperties.targetFramework == TestFramework.JUNIT){ + return new JUnitMessagingMethodBodyBuilder(stubContent) + } + return new SpockMessagingMethodBodyBuilder(stubContent) + } if (configProperties.testMode == TestMode.MOCKMVC && configProperties.targetFramework == TestFramework.JUNIT){ return new MockMvcJUnitMethodBodyBuilder(stubContent) } @@ -56,9 +57,9 @@ class MethodBuilder { if (configProperties.targetFramework == TestFramework.JUNIT){ return new JaxRsClientJUnitMethodBodyBuilder(stubContent) } - return new JaxRsClientSpockMethodBodyBuilder(stubContent) + return new JaxRsClientSpockMethodRequestProcessingBodyBuilder(stubContent) } - return new MockMvcSpockMethodBodyBuilder(stubContent) + return new MockMvcSpockMethodRequestProcessingBodyBuilder(stubContent) } } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBodyBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/MockMvcSpockMethodRequestProcessingBodyBuilder.groovy similarity index 88% rename from accurest-core/src/main/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBodyBuilder.groovy rename to accurest-core/src/main/groovy/io/codearte/accurest/builder/MockMvcSpockMethodRequestProcessingBodyBuilder.groovy index 80ee1169b2..bf3cdeb509 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/builder/MockMvcSpockMethodBodyBuilder.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/MockMvcSpockMethodRequestProcessingBodyBuilder.groovy @@ -10,9 +10,9 @@ import java.util.regex.Pattern @PackageScope @TypeChecked -class MockMvcSpockMethodBodyBuilder extends SpockMethodBodyBuilder { +class MockMvcSpockMethodRequestProcessingBodyBuilder extends SpockMethodRequestProcessingBodyBuilder { - MockMvcSpockMethodBodyBuilder(GroovyDsl stubDefinition) { + MockMvcSpockMethodRequestProcessingBodyBuilder(GroovyDsl stubDefinition) { super(stubDefinition) } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/RequestProcessingMethodBodyBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/RequestProcessingMethodBodyBuilder.groovy new file mode 100644 index 0000000000..6f8232c68e --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/RequestProcessingMethodBodyBuilder.groovy @@ -0,0 +1,152 @@ +package io.codearte.accurest.builder + +import groovy.json.JsonOutput +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.MatchingStrategy +import io.codearte.accurest.dsl.internal.NamedProperty +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.Url +import io.codearte.accurest.util.ContentType +import io.codearte.accurest.util.MapConverter + +import static io.codearte.accurest.util.ContentUtils.recognizeContentTypeFromContent +import static io.codearte.accurest.util.ContentUtils.recognizeContentTypeFromHeader + +/** + * @author Olga Maciaszek-Sharma + * @since 2016-02-17 + */ +@TypeChecked +@PackageScope +abstract class RequestProcessingMethodBodyBuilder extends MethodBodyBuilder { + + protected final Request request + protected final Response response + + RequestProcessingMethodBodyBuilder(GroovyDsl stubDefinition) { + this.request = stubDefinition.request + this.response = stubDefinition.response + } + + protected abstract String getInputString(Request request) + + @Override + protected boolean hasGivenSection() { + return request.headers || request.body + } + + 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 void processInput(BlockBuilder bb) { + request.headers?.collect { Header header -> + bb.addLine(getHeaderString(header)) + } + if (request.body) { + bb.addLine(getBodyString(bodyAsString)) + } + if (request.multipart) { + multipartParameters?.each { Map.Entry entry -> bb.addLine(getMultipartParameterLine(entry)) } + } + } + + protected void when(BlockBuilder bb) { + bb.addLine(getInputString(request)) + bb.indent() + + String url = buildUrl(request) + String method = request.method.serverValue.toString().toLowerCase() + + bb.addLine(/.${method}("$url")/) + addColonIfRequired(bb) + bb.unindent() + } + + protected void then(BlockBuilder bb) { + validateResponseCodeBlock(bb) + if (response.headers) { + validateResponseHeadersBlock(bb) + } + if (response.body) { + bb.endBlock() + bb.addLine(addCommentSignIfRequired('and:')).startBlock() + validateResponseBodyBlock(bb, response.body.serverValue) + } + } + + protected ContentType getResponseContentType() { + ContentType contentType = recognizeContentTypeFromHeader(response.headers) + if (contentType == ContentType.UNKNOWN) { + contentType = recognizeContentTypeFromContent(response.body.serverValue) + } + return contentType + } + + protected String getBodyAsString() { + Object bodyValue = extractServerValueFromBody(request.body.serverValue) + String json = new JsonOutput().toJson(bodyValue) + json = convertUnicodeEscapesIfRequired(json) + return trimRepeatedQuotes(json) + } + + protected Map getMultipartParameters() { + return (Map) request?.multipart?.serverValue + } + + protected ContentType getRequestContentType() { + ContentType contentType = recognizeContentTypeFromHeader(request.headers) + if (contentType == ContentType.UNKNOWN) { + contentType = recognizeContentTypeFromContent(request.body.serverValue) + } + return contentType + } + + protected String buildUrl(Request request) { + if (request.url) + return getTestSideValue(buildUrlFromUrlPath(request.url)) + if (request.urlPath) + return getTestSideValue(buildUrlFromUrlPath(request.urlPath)) + throw new IllegalStateException("URL is not set!") + } + + @TypeChecked(TypeCheckingMode.SKIP) + protected String buildUrlFromUrlPath(Url url) { + if (hasQueryParams(url)) { + String params = url.queryParameters.parameters + .findAll(this.&allowedQueryParameter) + .inject([] as List) { List result, QueryParameter param -> + result << "${param.name}=${resolveParamValue(param).toString()}" + } + .join('&') + return "${MapConverter.getTestSideValues(url.serverValue)}?$params" + } + return MapConverter.getTestSideValues(url.serverValue) + } + + protected String getMultipartParameterLine(Map.Entry parameter) { + if (parameter.value instanceof NamedProperty) { + return ".multiPart(${getMultipartFileParameterContent(parameter.key, (NamedProperty) parameter.value)})" + } + return getParameterString(parameter) + } + + + private boolean hasQueryParams(Url url) { + return url.queryParameters + } +} diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMessagingMethodBodyBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMessagingMethodBodyBuilder.groovy new file mode 100644 index 0000000000..6e89373010 --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMessagingMethodBodyBuilder.groovy @@ -0,0 +1,161 @@ +package io.codearte.accurest.builder + +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 io.codearte.accurest.dsl.internal.Input +import io.codearte.accurest.dsl.internal.NamedProperty + +import java.util.regex.Pattern +/** + * @author Jakub Kubrynski + */ +@PackageScope +@TypeChecked +class SpockMessagingMethodBodyBuilder extends MessagingMethodBodyBuilder { + + SpockMessagingMethodBodyBuilder(GroovyDsl stubDefinition) { + super(stubDefinition) + } + + @Override + protected String getInputString(Input request) { + if (request.triggeredBy) { + return request.triggeredBy.executionCommand + } + return "accurestMessaging.send(inputMessage, '${request.messageFrom}')" + } + + @Override + protected String getResponseBodyPropertyComparisonString(String property, String value) { + return "responseBody$property == \"${value}\"" + } + + @Override + protected void processBodyElement(BlockBuilder blockBuilder, String property, ExecutionProperty exec) { + blockBuilder.addLine("${exec.insertValue("parsedJson.read('\\\$$property')")}") + } + + @Override + protected void processBodyElement(BlockBuilder blockBuilder, String property, Map.Entry entry) { + processBodyElement(blockBuilder, property + "." + entry.key, entry.value) + } + + @Override + protected void processHeaderElement(BlockBuilder blockBuilder, String property, ExecutionProperty exec) { + blockBuilder.addLine("${exec.insertValue("response.getHeader(\'$property\')")}") + } + + @Override + protected void processHeaderElement(BlockBuilder blockBuilder, String property, String value) { + blockBuilder.addLine("response.getHeader('$property') ${convertHeaderComparison(value)}") + } + + @Override + protected void processHeaderElement(BlockBuilder blockBuilder, String property, Pattern value) { + blockBuilder.addLine("response.getHeader('$property') ${convertHeaderComparison(value)}") + } + + @Override + protected void validateResponseCodeBlock(BlockBuilder bb) { + if (outputMessage) { + bb.addLine("""def response = accurestMessaging.receiveMessage('${outputMessage.sentTo}')""") + } else { + bb.addLine('noExceptionThrown()') + } + } + + @Override + protected void validateResponseHeadersBlock(BlockBuilder bb) { + outputMessage.headers?.collect { Header header -> + processHeaderElement(bb, header.name, header.serverValue) + } + } + + @Override + protected String getResponseAsString() { + return 'accurestObjectMapper.writeValueAsString(response.payload)' + } + + @Override + protected String addCommentSignIfRequired(String baseString) { + return baseString + } + + @Override + protected BlockBuilder addColonIfRequired(BlockBuilder blockBuilder) { + return blockBuilder + } + + @Override + protected String getPropertyInListString(String property, Integer listIndex) { + "$property[$listIndex]" ?: '' + } + + @Override + protected String convertUnicodeEscapesIfRequired(String json) { + return StringEscapeUtils.unescapeJavaScript(json) + } + + @Override + protected String getParsedXmlResponseBodyString(String responseString) { + return "def responseBody = new XmlSlurper().parseText($responseString)" + } + + @Override + protected String getSimpleResponseBodyString(String responseString) { + return "def responseBody = ($responseString)" + } + + @Override + protected String getInputString() { + String request = 'def inputMessage = accurestMessaging.create(' + if (inputMessage.messageBody) { + request = "${request}'''${bodyAsString}'''\n\t\t" + } + if (inputMessage.messageHeaders) { + request = "${request},[\n" + } + def headers = [] + inputMessage.messageHeaders?.collect { Header header -> + headers << "\t\t\t${getHeaderString(header)}" + } + request = "${request}${headers.join(',\n')}" + if (inputMessage.messageHeaders) { + request = "${request}\n\t\t]" + } + return "${request})" + } + + @Override + protected String getHeaderString(Header header) { + return "'${getTestSideValue(header.name)}': '${getTestSideValue(header.serverValue)}'" + } + + @Override + protected String getBodyString(String bodyAsString) { + return '' + } + + @Override + protected String getMultipartFileParameterContent(String propertyName, NamedProperty propertyValue) { + return '' + } + + @Override + protected String getParameterString(Map.Entry parameter) { + return '' + } + + protected String convertHeaderComparison(String headerValue) { + return " == '$headerValue'" + } + + protected String convertHeaderComparison(Pattern headerValue) { + return "==~ java.util.regex.Pattern.compile('$headerValue')" + } + +} diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodRequestProcessingBodyBuilder.groovy similarity index 91% rename from accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy rename to accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodRequestProcessingBodyBuilder.groovy index b7a3f22e60..b9d1aedf86 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodBodyBuilder.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/builder/SpockMethodRequestProcessingBodyBuilder.groovy @@ -18,9 +18,9 @@ import static io.codearte.accurest.util.ContentUtils.getGroovyMultipartFileParam */ @PackageScope @TypeChecked -abstract class SpockMethodBodyBuilder extends MethodBodyBuilder { +abstract class SpockMethodRequestProcessingBodyBuilder extends RequestProcessingMethodBodyBuilder { - SpockMethodBodyBuilder(GroovyDsl stubDefinition) { + SpockMethodRequestProcessingBodyBuilder(GroovyDsl stubDefinition) { super(stubDefinition) } @@ -70,12 +70,12 @@ abstract class SpockMethodBodyBuilder extends MethodBodyBuilder { } @Override - protected String getResponseString(Request request) { + protected String getInputString(Request request) { return 'def response = given().spec(request)' } @Override - protected String getRequestString() { + protected String getInputString() { return 'def request = given()' } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/config/AccurestConfigProperties.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/config/AccurestConfigProperties.groovy index 9e89c8909e..ae160a69d4 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/config/AccurestConfigProperties.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/config/AccurestConfigProperties.groovy @@ -74,4 +74,9 @@ class AccurestConfigProperties { * You can then mention them in your packaging task to create jar with stubs */ File stubsOutputDir + + /** + * Which version of Accurest Messaging Core to use + */ + String accurestMessagingCoreVersion = "+" } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/GroovyDsl.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/GroovyDsl.groovy index af046d188f..97b023b34e 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/GroovyDsl.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/GroovyDsl.groovy @@ -3,6 +3,8 @@ package io.codearte.accurest.dsl import groovy.transform.EqualsAndHashCode import groovy.transform.ToString import groovy.transform.TypeChecked +import io.codearte.accurest.dsl.internal.Input +import io.codearte.accurest.dsl.internal.OutputMessage import io.codearte.accurest.dsl.internal.Request import io.codearte.accurest.dsl.internal.Response @@ -14,6 +16,9 @@ class GroovyDsl { Integer priority Request request Response response + String label + Input inputMessage + OutputMessage outputMessage static GroovyDsl make(Closure closure) { GroovyDsl dsl = new GroovyDsl() @@ -26,6 +31,10 @@ class GroovyDsl { this.priority = priority } + void label(String label) { + this.label = label + } + void request(@DelegatesTo(Request) Closure closure) { this.request = new Request() closure.delegate = request @@ -38,4 +47,16 @@ class GroovyDsl { closure() } + void input(@DelegatesTo(Input) Closure closure) { + this.inputMessage = new Input() + closure.delegate = inputMessage + closure() + } + + void outputMessage(@DelegatesTo(OutputMessage) Closure closure) { + this.outputMessage = new OutputMessage() + closure.delegate = outputMessage + closure() + } + } diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockRequestStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockRequestStubStrategy.groovy index e3736d0a7a..f5c4f6945c 100755 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockRequestStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockRequestStubStrategy.groovy @@ -40,6 +40,9 @@ class WireMockRequestStubStrategy extends BaseWireMockStubStrategy { @PackageScope RequestPattern buildClientRequestContent() { + if(!request) { + return null + } RequestPattern requestPattern = new RequestPattern() appendMethod(requestPattern) appendHeaders(requestPattern) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockResponseStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockResponseStubStrategy.groovy index 8dda4777bd..1d19dfcb38 100755 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockResponseStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockResponseStubStrategy.groovy @@ -27,6 +27,9 @@ class WireMockResponseStubStrategy extends BaseWireMockStubStrategy { @PackageScope ResponseDefinition buildClientResponseContent() { + if(!response) { + return null + } ResponseDefinitionBuilder builder = new ResponseDefinitionBuilder() .withStatus(response.status.clientValue as Integer) appendHeaders(builder) diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockStubStrategy.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockStubStrategy.groovy index 9133b85067..2f1cb8b0f4 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockStubStrategy.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/WireMockStubStrategy.groovy @@ -29,14 +29,20 @@ class WireMockStubStrategy { @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 + if (!request || !response) { + return '' + } + if (contract.order != null) { stubMapping.scenarioName = "Scenario_" + rootName stubMapping.requiredScenarioState = contract.order == 0 ? STEP_START : STEP_PREFIX + contract.order diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Input.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Input.groovy new file mode 100644 index 0000000000..8cd7fe06f4 --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/Input.groovy @@ -0,0 +1,77 @@ +package io.codearte.accurest.dsl.internal + +import groovy.transform.CompileStatic +import groovy.transform.EqualsAndHashCode +import groovy.transform.ToString +import groovy.transform.TypeChecked + +@TypeChecked +@EqualsAndHashCode +@ToString(includePackage = false, includeNames = true) +class Input extends Common { + + String messageFrom + ExecutionProperty triggeredBy + Headers messageHeaders + BodyType messageBody + ExecutionProperty assertThat + + Input() {} + + Input(Input input) { + this.messageFrom = input.messageFrom + this.messageHeaders = input.messageHeaders + this.messageBody = input.messageBody + } + + void messageFrom(String messageFrom) { + this.messageFrom = messageFrom + } + + void triggeredBy(String triggeredBy) { + this.triggeredBy = new ExecutionProperty(triggeredBy) + } + + BodyType messageBody(Object bodyAsValue) { + this.messageBody = new BodyType(bodyAsValue) + } + + void messageHeaders(@DelegatesTo(Headers) Closure closure) { + this.messageHeaders = new Headers() + closure.delegate = messageHeaders + closure() + } + + public static class BodyType extends DslProperty { + + BodyType(Object clientValue, Object serverValue) { + super(clientValue, serverValue) + } + + BodyType(Object singleValue) { + super(singleValue) + } + } + + void assertThat(String assertThat) { + this.assertThat = new ExecutionProperty(assertThat) + } +} + +@CompileStatic +@EqualsAndHashCode +@ToString(includePackage = false) +class ServerInput extends Input { + ServerInput(Input request) { + super(request) + } +} + +@CompileStatic +@EqualsAndHashCode +@ToString(includePackage = false) +class ClientInput extends Input { + ClientInput(Input request) { + super(request) + } +} diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/OutputMessage.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/OutputMessage.groovy new file mode 100644 index 0000000000..2a8c96249f --- /dev/null +++ b/accurest-core/src/main/groovy/io/codearte/accurest/dsl/internal/OutputMessage.groovy @@ -0,0 +1,65 @@ +package io.codearte.accurest.dsl.internal + +import groovy.transform.CompileStatic +import groovy.transform.EqualsAndHashCode +import groovy.transform.ToString +import groovy.transform.TypeChecked + +@TypeChecked +@EqualsAndHashCode +@ToString(includePackage = false, includeNames = true) +class OutputMessage extends Common { + + String sentTo + Headers headers + DslProperty body + ExecutionProperty assertThat + + OutputMessage() {} + + OutputMessage(OutputMessage outputMessage) { + this.sentTo = outputMessage.sentTo + this.headers = outputMessage.headers + this.body = outputMessage.body + } + + void sentTo(String sentTo) { + this.sentTo = sentTo + } + + void body(Object bodyAsValue) { + this.body = new DslProperty(bodyAsValue) + } + + void body(DslProperty bodyAsValue) { + this.body = bodyAsValue + } + + void headers(@DelegatesTo(Headers) Closure closure) { + this.headers = new Headers() + closure.delegate = headers + closure() + } + + void assertThat(String assertThat) { + this.assertThat = new ExecutionProperty(assertThat) + } +} + +@CompileStatic +@EqualsAndHashCode +@ToString(includePackage = false) +class ServerOutputMessage extends OutputMessage { + ServerOutputMessage(OutputMessage request) { + super(request) + } +} + +@CompileStatic +@EqualsAndHashCode +@ToString(includePackage = false) +class ClientOutputMessage extends OutputMessage { + ClientOutputMessage(OutputMessage request) { + super(request) + } +} diff --git a/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy b/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy index d2916f36d5..b8608f767a 100644 --- a/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy +++ b/accurest-core/src/main/groovy/io/codearte/accurest/util/ContentUtils.groovy @@ -287,6 +287,15 @@ class ContentUtils { return ContentType.JSON } + public static ContentType recognizeContentTypeFromContent(String string) { + try { + new JsonSlurper().parseText(string) + return ContentType.JSON + } catch (Exception e){ + return ContentType.UNKNOWN + } + } + public static ContentType recognizeContentTypeFromContent(Object gstring) { return ContentType.UNKNOWN } diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/BookReturned.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/BookReturned.groovy new file mode 100644 index 0000000000..9c2656d426 --- /dev/null +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/BookReturned.groovy @@ -0,0 +1,14 @@ +package io.codearte.accurest.builder + +import com.fasterxml.jackson.annotation.JsonCreator +import groovy.transform.CompileStatic + +@CompileStatic +class BookReturned implements Serializable { + final String bookName + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + BookReturned(String bookName) { + this.bookName = bookName + } +} diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/JaxRsClientMethodBuilderSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/JaxRsClientMethodBuilderSpec.groovy index f659c0bab5..0b07ec4759 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/builder/JaxRsClientMethodBuilderSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/JaxRsClientMethodBuilderSpec.groovy @@ -37,7 +37,7 @@ class JaxRsClientMethodBuilderSpec extends Specification implements WireMockStub stubMappingIsValidWireMockStub(contractDsl) where: methodBuilderName | methodBuilder - "JaxRsClientSpockMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientSpockMethodBodyBuilder(dsl) } + "JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl) } "JaxRsClientJUnitMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl) } } @@ -71,7 +71,7 @@ class JaxRsClientMethodBuilderSpec extends Specification implements WireMockStub stubMappingIsValidWireMockStub(new WireMockStubStrategy("Test", new Contract(null, false, 0, null), contractDsl).toWireMockClientStub()) where: methodBuilderName | methodBuilder - "JaxRsClientSpockMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientSpockMethodBodyBuilder(dsl) } + "JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl) } "JaxRsClientJUnitMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl) } } @@ -107,7 +107,7 @@ class JaxRsClientMethodBuilderSpec extends Specification implements WireMockStub stubMappingIsValidWireMockStub(new WireMockStubStrategy("Test", new Contract(null, false, 0, null), contractDsl).toWireMockClientStub()) where: methodBuilderName | methodBuilder - "JaxRsClientSpockMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientSpockMethodBodyBuilder(dsl) } + "JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl) } "JaxRsClientJUnitMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl) } } @@ -136,9 +136,9 @@ class JaxRsClientMethodBuilderSpec extends Specification implements WireMockStub and: stubMappingIsValidWireMockStub(contractDsl) where: - methodBuilderName | methodBuilder | bodyString - "JaxRsClientSpockMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientSpockMethodBodyBuilder(dsl) } | """entity('{\"items\":[\"HOP\"]}', 'application/json')""" - "JaxRsClientJUnitMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl) } | 'entity("{\\"items\\":[\\"HOP\\"]}", "application/json")' + methodBuilderName | methodBuilder | bodyString + "JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl) } | """entity('{\"items\":[\"HOP\"]}', 'application/json')""" + "JaxRsClientJUnitMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl) } | 'entity("{\\"items\\":[\\"HOP\\"]}", "application/json")' } @Issue("#88") @@ -166,9 +166,9 @@ class JaxRsClientMethodBuilderSpec extends Specification implements WireMockStub and: stubMappingIsValidWireMockStub(contractDsl) where: - methodBuilderName | methodBuilder | bodyString - "JaxRsClientSpockMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientSpockMethodBodyBuilder(dsl) } | """entity('property1=VAL1', 'application/octet-stream')""" - "JaxRsClientJUnitMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl) } | 'entity("\\"property1=VAL1\\"", "application/octet-stream")' + methodBuilderName | methodBuilder | bodyString + "JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl) } | """entity('property1=VAL1', 'application/octet-stream')""" + "JaxRsClientJUnitMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl) } | 'entity("\\"property1=VAL1\\"", "application/octet-stream")' } @Unroll @@ -201,7 +201,7 @@ class JaxRsClientMethodBuilderSpec extends Specification implements WireMockStub stubMappingIsValidWireMockStub(contractDsl) where: methodBuilderName | methodBuilder - "JaxRsClientSpockMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientSpockMethodBodyBuilder(dsl) } + "JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl) } "JaxRsClientJUnitMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl) } } @@ -234,7 +234,7 @@ class JaxRsClientMethodBuilderSpec extends Specification implements WireMockStub stubMappingIsValidWireMockStub(contractDsl) where: methodBuilderName | methodBuilder - "JaxRsClientSpockMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientSpockMethodBodyBuilder(dsl) } + "JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl) } "JaxRsClientJUnitMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl) } } @@ -267,7 +267,7 @@ class JaxRsClientMethodBuilderSpec extends Specification implements WireMockStub stubMappingIsValidWireMockStub(contractDsl) where: methodBuilderName | methodBuilder - "JaxRsClientSpockMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientSpockMethodBodyBuilder(dsl) } + "JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl) } "JaxRsClientJUnitMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl) } } @@ -306,7 +306,7 @@ class JaxRsClientMethodBuilderSpec extends Specification implements WireMockStub stubMappingIsValidWireMockStub(contractDsl) where: methodBuilderName | methodBuilder - "JaxRsClientSpockMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientSpockMethodBodyBuilder(dsl) } + "JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl) } "JaxRsClientJUnitMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl) } } @@ -339,7 +339,7 @@ class JaxRsClientMethodBuilderSpec extends Specification implements WireMockStub stubMappingIsValidWireMockStub(contractDsl) where: methodBuilderName | methodBuilder - "JaxRsClientSpockMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientSpockMethodBodyBuilder(dsl) } + "JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl) } "JaxRsClientJUnitMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl) } } @@ -367,9 +367,9 @@ class JaxRsClientMethodBuilderSpec extends Specification implements WireMockStub and: stubMappingIsValidWireMockStub(contractDsl) where: - methodBuilderName | methodBuilder | requestString - "JaxRsClientSpockMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientSpockMethodBodyBuilder(dsl) } | "request('text/plain')" - "JaxRsClientJUnitMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl) } | 'request("text/plain")' + methodBuilderName | methodBuilder | requestString + "JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl) } | "request('text/plain')" + "JaxRsClientJUnitMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl) } | 'request("text/plain")' } @Unroll @@ -401,9 +401,9 @@ class JaxRsClientMethodBuilderSpec extends Specification implements WireMockStub and: stubMappingIsValidWireMockStub(contractDsl) where: - methodBuilderName | methodBuilder | requestStrings - "JaxRsClientSpockMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientSpockMethodBodyBuilder(dsl) } | ["""entity('', 'text/plain')""", """header('Timer', '123')"""] - "JaxRsClientJUnitMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl) } | ['entity("\\"\\"", "text/plain")', 'header("Timer", "123")'] + methodBuilderName | methodBuilder | requestStrings + "JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl) } | ["""entity('', 'text/plain')""", """header('Timer', '123')"""] + "JaxRsClientJUnitMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl) } | ['entity("\\"\\"", "text/plain")', 'header("Timer", "123")'] } @Unroll @@ -456,9 +456,9 @@ class JaxRsClientMethodBuilderSpec extends Specification implements WireMockStub and: stubMappingIsValidWireMockStub(contractDsl) where: - methodBuilderName | methodBuilder | modifyStringIfRequired - "JaxRsClientSpockMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientSpockMethodBodyBuilder(dsl) } | { String paramString -> paramString } - "JaxRsClientJUnitMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl) } | { String paramString -> paramString.replace("'", "\"") } + methodBuilderName | methodBuilder | modifyStringIfRequired + "JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl) } | { String paramString -> paramString } + "JaxRsClientJUnitMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl) } | { String paramString -> paramString.replace("'", "\"") } } @Issue('#169') @@ -512,9 +512,9 @@ class JaxRsClientMethodBuilderSpec extends Specification implements WireMockStub and: stubMappingIsValidWireMockStub(contractDsl) where: - methodBuilderName | methodBuilder | modifyStringIfRequired - "JaxRsClientSpockMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientSpockMethodBodyBuilder(dsl) } | { String paramString -> paramString } - "JaxRsClientJUnitMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl) } | { String paramString -> paramString.replace("'", "\"") } + methodBuilderName | methodBuilder | modifyStringIfRequired + "JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl) } | { String paramString -> paramString } + "JaxRsClientJUnitMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl) } | { String paramString -> paramString.replace("'", "\"") } } @Unroll @@ -541,9 +541,9 @@ class JaxRsClientMethodBuilderSpec extends Specification implements WireMockStub and: stubMappingIsValidWireMockStub(contractDsl) where: - methodBuilderName | methodBuilder | bodyString - "JaxRsClientSpockMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientSpockMethodBodyBuilder(dsl) } | "entity('', 'application/octet-stream')" - "JaxRsClientJUnitMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl) } | 'entity("\\"\\"", "application/octet-stream"' + methodBuilderName | methodBuilder | bodyString + "JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl) } | "entity('', 'application/octet-stream')" + "JaxRsClientJUnitMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl) } | 'entity("\\"\\"", "application/octet-stream"' } @Unroll @@ -570,9 +570,9 @@ class JaxRsClientMethodBuilderSpec extends Specification implements WireMockStub and: stubMappingIsValidWireMockStub(contractDsl) where: - methodBuilderName | methodBuilder | bodyDefinitionString | bodyEvaluationString - "JaxRsClientSpockMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientSpockMethodBodyBuilder(dsl) } | "String responseAsString = response.readEntity(String)" | 'responseBody == "test"' - "JaxRsClientJUnitMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl) } | 'Object responseBody = (responseAsString);' | 'assertThat(responseBody).isEqualTo("test");' + methodBuilderName | methodBuilder | bodyDefinitionString | bodyEvaluationString + "JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl) } | "String responseAsString = response.readEntity(String)" | 'responseBody == "test"' + "JaxRsClientJUnitMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl) } | 'Object responseBody = (responseAsString);' | 'assertThat(responseBody).isEqualTo("test");' } @Issue('#171') @@ -604,9 +604,9 @@ class JaxRsClientMethodBuilderSpec extends Specification implements WireMockStub and: stubMappingIsValidWireMockStub(contractDsl) where: - methodBuilderName | methodBuilder | methodString - "JaxRsClientSpockMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientSpockMethodBodyBuilder(dsl) } | ".method('GET')" - "JaxRsClientJUnitMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl) } | 'method("GET")' + methodBuilderName | methodBuilder | methodString + "JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl) } | ".method('GET')" + "JaxRsClientJUnitMethodBodyBuilder" | { GroovyDsl dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl) } | 'method("GET")' } } diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/MessagingMethodBodyBuilderSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MessagingMethodBodyBuilderSpec.groovy new file mode 100644 index 0000000000..7d9c1020b5 --- /dev/null +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MessagingMethodBodyBuilderSpec.groovy @@ -0,0 +1,252 @@ +package io.codearte.accurest.builder + +import io.codearte.accurest.dsl.GroovyDsl +import spock.lang.Specification +/** + * @author Marcin Grzejszczak + */ +class MessagingMethodBodyBuilderSpec extends Specification { + + def "should work for triggered based messaging with Spock"() { + given: + def contractDsl = GroovyDsl.make { + label 'some_label' + input { + triggeredBy('bookReturnedTriggered()') + } + outputMessage { + sentTo('activemq:output') + body('''{ "bookName" : "foo" }''') + headers { + header('BOOK-NAME', 'foo') + } + } + } + MethodBodyBuilder builder = new SpockMessagingMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + def test = blockBuilder.toString() + then: + stripped(test) == stripped(''' + when: + bookReturnedTriggered() + + then: + def response = accurestMessaging.receiveMessage('activemq:output') + response.getHeader('BOOK-NAME') == 'foo' + and: + DocumentContext parsedJson = JsonPath.parse(accurestObjectMapper.writeValueAsString(response.payload)) + assertThatJson(parsedJson).field("bookName").isEqualTo("foo") + ''') + } + + def "should work for triggered based messaging with JUnit"() { + given: + def contractDsl = GroovyDsl.make { + label 'some_label' + input { + triggeredBy('bookReturnedTriggered()') + } + outputMessage { + sentTo('activemq:output') + body('''{ "bookName" : "foo" }''') + headers { + header('BOOK-NAME', 'foo') + } + } + } + MethodBodyBuilder builder = new JUnitMessagingMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + def test = blockBuilder.toString() + then: + stripped(test) == stripped(''' + // when: + bookReturnedTriggered(); + + // then: + AccurestMessage response = accurestMessaging.receiveMessage("activemq:output"); + assertThat(response.getHeader("BOOK-NAME")).isEqualTo("foo"); + // and: + DocumentContext parsedJson = JsonPath.parse(accurestObjectMapper.writeValueAsString(response.getPayload())); + assertThatJson(parsedJson).field("bookName").isEqualTo("foo"); + ''') + } + + private String stripped(String text) { + return text.stripIndent().stripMargin().replace('\t', '').replace('\n', '') + } + + def "should generate tests triggered by a message for Spock"() { + given: + def contractDsl = GroovyDsl.make { + label 'some_label' + input { + messageFrom('jms:input') + messageBody([ + bookName: 'foo' + ]) + messageHeaders { + header('sample', 'header') + } + } + outputMessage { + sentTo('jms:output') + body([ + bookName: 'foo' + ]) + headers { + header('BOOK-NAME', 'foo') + } + } + } + MethodBodyBuilder builder = new SpockMessagingMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + def test = blockBuilder.toString() + then: + stripped(test) == stripped(''' + given: + def inputMessage = accurestMessaging.create( + \'\'\'{"bookName":"foo"}\'\'\', + ['sample': 'header'] + ) + + when: + accurestMessaging.send(inputMessage, 'jms:input') + + then: + def response = accurestMessaging.receiveMessage('jms:output') + response.getHeader('BOOK-NAME') == 'foo' + and: + DocumentContext parsedJson = JsonPath.parse(accurestObjectMapper.writeValueAsString(response.payload)) + assertThatJson(parsedJson).field("bookName").isEqualTo("foo") + ''') + } + + def "should generate tests triggered by a message for JUnit"() { + given: + def contractDsl = GroovyDsl.make { + label 'some_label' + input { + messageFrom('jms:input') + messageBody([ + bookName: 'foo' + ]) + messageHeaders { + header('sample', 'header') + } + } + outputMessage { + sentTo('jms:output') + body([ + bookName: 'foo' + ]) + headers { + header('BOOK-NAME', 'foo') + } + } + } + MethodBodyBuilder builder = new JUnitMessagingMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + def test = blockBuilder.toString() + then: + stripped(test) == stripped(''' + // given: + AccurestMessage inputMessage = accurestMessaging.create( + "{\\"bookName\\":\\"foo\\"}" + , headers() + .header("sample", "header")); + + // when: + accurestMessaging.send(inputMessage, "jms:input"); + + // then: + AccurestMessage response = accurestMessaging.receiveMessage("jms:output"); + assertThat(response.getHeader("BOOK-NAME")).isEqualTo("foo"); + // and: + DocumentContext parsedJson = JsonPath.parse(accurestObjectMapper.writeValueAsString(response.getPayload())); + assertThatJson(parsedJson).field("bookName").isEqualTo("foo"); + + ''') + } + + def "should generate tests without destination, triggered by a message"() { + given: + def contractDsl = GroovyDsl.make { + label 'some_label' + input { + messageFrom('jms:delete') + messageBody([ + bookName: 'foo' + ]) + messageHeaders { + header('sample', 'header') + } + assertThat('bookWasDeleted()') + } + } + MethodBodyBuilder builder = new SpockMessagingMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + def test = blockBuilder.toString() + then: + stripped(test) == stripped(''' + given: + def inputMessage = accurestMessaging.create( + \'\'\'{"bookName":"foo"}\'\'\', + ['sample': 'header'] + ) + + when: + accurestMessaging.send(inputMessage, 'jms:delete') + + then: + noExceptionThrown() + bookWasDeleted() + ''') + } + + def "should generate tests without destination, triggered by a message for JUnit"() { + given: + def contractDsl = GroovyDsl.make { + label 'some_label' + input { + messageFrom('jms:delete') + messageBody([ + bookName: 'foo' + ]) + messageHeaders { + header('sample', 'header') + } + assertThat('bookWasDeleted()') + } + } + MethodBodyBuilder builder = new JUnitMessagingMethodBodyBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + def test = blockBuilder.toString() + then: + stripped(test) == stripped(''' + // given: + AccurestMessage inputMessage = accurestMessaging.create( + "{\\"bookName\\":\\"foo\\"}" + , headers() + .header("sample", "header")); + + // when: + accurestMessaging.send(inputMessage, "jms:delete"); + + // then: + bookWasDeleted(); + ''') + } + +} diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/MessagingSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MessagingSpec.groovy new file mode 100644 index 0000000000..84ac3d5e27 --- /dev/null +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MessagingSpec.groovy @@ -0,0 +1,137 @@ +package io.codearte.accurest.builder + +import io.codearte.accurest.dsl.GroovyDsl +import spock.lang.Specification + +/** + + # TO CONSIDER + + - multiple messages can be sent out (for now let's focus on a single one) + - tests thanks to stream-test-binder will get executed in single threaded mode (no need for awaitility) + - do we need an explicit assertion of an input message (the one that enters the source?) or will + it be done in the processor + + - let's support only JSON payload and headers taken from message ATM + + + # TRIGGERING MESSAGES + + Triggering for the client side might be done via stub runner messaging module. + + ## JUNIT + + @ClassRule public static AccurestRule stubFinder = new AccurestRule() + .repoRoot(repoRoot()) + .downloadStub("io.codearte.accurest.stubs", "loanIssuance") + .downloadStub("io.codearte.accurest.stubs:fraudDetectionServer"); + + ## SPRING + + @Autowired StubFinder stubFinder + + ## TRIGGERING EXAMPLES + + //test: + + // execute all triggers for all artifacts + stubFinder.trigger() + // execute all triggers named 'some_label' for all artifacts + stubFinder.trigger("some_label") + // execute all triggers named 'some_label' for the artifact in Ivy notation + stubFinder.trigger("io.codearte.accurest.stubs:fraudDetectionServer", "some_label") + // execute all triggers named 'some_label' for the artifact with id ... + stubFinder.trigger("fraudDetectionServer", "some_label") + + if no label is provided then all triggers will get executed + + # CLIENT SIDE TEST EXAMPLE + + ## DSL + + GroovyDsl.make { + label 'some_label' + input { + triggeredBy(execute('method()')) + } + outputMessage { + onChannel('messageFrom') + messageBody("book returned") + } + } + + ## TEST + + @Test + public void run_some_test() { + // given + client.borrowABook(); + + // when - sends a message to the messageFrom provided in the "outputMessage" section of the DSL + stubFinder.trigger("client_returned_a_book"); + + // then + then(client).hasNoBooksBorrowed(); + } + + * + * @author Marcin Grzejszczak + */ +class MessagingSpec extends Specification { + + // client side: must have a possibility to "trigger" sending of a message to the given messageFrom + // server side: will run the method and await upon receiving message on the output messageFrom + def "should generate tests triggered by a method"() { + expect: + GroovyDsl.make { + label 'some_label' + input { + triggeredBy('bookReturnedTriggered()') + } + outputMessage { + sentTo('channel') + body('''{ "bookName" : "foo" }''') + headers { + header('BOOK-NAME', 'foo') + } + } + } + } + + // client side: if sends a message to input.messageFrom then message will be sent to output.messageFrom + // server side: will send a message to input, verify the message contents and await upon receiving message on the output messageFrom + def "should generate tests triggered by a message"() { + expect: + GroovyDsl.make { + input { + messageFrom('input') + messageBody("some message") + messageHeaders { + header('key', 'value') + } + } + outputMessage { + sentTo('output') + body('message') + headers { + header('anotherkey', 'anothervalue') + } + } + } + } + + // client side: if sends a message to input.messageFrom then message if matches will get consumed + // server side: will send a message to input and verify the message contents + def "should generate tests without destination, triggered by a message"() { + expect: + GroovyDsl.make { + input { + messageFrom('input') + messageBody("some message") + messageHeaders { + header('key', 'value') + } + } + } + } +} diff --git a/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcMethodBodyBuilderSpec.groovy b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcMethodBodyBuilderSpec.groovy index 349b244f97..ed6ee0dd1f 100644 --- a/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcMethodBodyBuilderSpec.groovy +++ b/accurest-core/src/test/groovy/io/codearte/accurest/builder/MockMvcMethodBodyBuilderSpec.groovy @@ -100,7 +100,7 @@ class MockMvcMethodBodyBuilderSpec extends Specification implements WireMockStub stubMappingIsValidWireMockStub(contractDsl) where: methodBuilderName | methodBuilder - "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodBodyBuilder(dsl) } + "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl) } "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } } @@ -134,7 +134,7 @@ class MockMvcMethodBodyBuilderSpec extends Specification implements WireMockStub stubMappingIsValidWireMockStub(contractDsl) where: methodBuilderName | methodBuilder - "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodBodyBuilder(dsl) } + "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl) } "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } } @@ -170,7 +170,7 @@ class MockMvcMethodBodyBuilderSpec extends Specification implements WireMockStub stubMappingIsValidWireMockStub(contractDsl) where: methodBuilderName | methodBuilder - "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodBodyBuilder(dsl) } + "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl) } "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } } @@ -199,9 +199,9 @@ class MockMvcMethodBodyBuilderSpec extends Specification implements WireMockStub and: stubMappingIsValidWireMockStub(contractDsl) where: - methodBuilderName | methodBuilder | bodyString - "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodBodyBuilder(dsl) } | """.body('''{\"items\":[\"HOP\"]}''')""" - "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } | '.body("{\\"items\\":[\\"HOP\\"]}")' + methodBuilderName | methodBuilder | bodyString + "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl) } | """.body('''{\"items\":[\"HOP\"]}''')""" + "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } | '.body("{\\"items\\":[\\"HOP\\"]}")' } @Issue("#88") @@ -229,9 +229,9 @@ class MockMvcMethodBodyBuilderSpec extends Specification implements WireMockStub and: stubMappingIsValidWireMockStub(contractDsl) where: - methodBuilderName | methodBuilder | bodyString - "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodBodyBuilder(dsl) } | """.body('''property1=VAL1''')""" - "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } | '.body("\\"property1=VAL1\\"")' + methodBuilderName | methodBuilder | bodyString + "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl) } | """.body('''property1=VAL1''')""" + "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } | '.body("\\"property1=VAL1\\"")' } @Issue("185") @@ -264,7 +264,7 @@ class MockMvcMethodBodyBuilderSpec extends Specification implements WireMockStub stubMappingIsValidWireMockStub(contractDsl) where: methodBuilderName | methodBuilder - "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodBodyBuilder(dsl) } + "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl) } "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } } @@ -298,7 +298,7 @@ class MockMvcMethodBodyBuilderSpec extends Specification implements WireMockStub stubMappingIsValidWireMockStub(contractDsl) where: methodBuilderName | methodBuilder - "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodBodyBuilder(dsl) } + "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl) } "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } } @@ -331,7 +331,7 @@ class MockMvcMethodBodyBuilderSpec extends Specification implements WireMockStub stubMappingIsValidWireMockStub(contractDsl) where: methodBuilderName | methodBuilder - "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodBodyBuilder(dsl) } + "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl) } "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } } @@ -364,7 +364,7 @@ class MockMvcMethodBodyBuilderSpec extends Specification implements WireMockStub stubMappingIsValidWireMockStub(contractDsl) where: methodBuilderName | methodBuilder - "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodBodyBuilder(dsl) } + "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl) } "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } } @@ -401,7 +401,7 @@ class MockMvcMethodBodyBuilderSpec extends Specification implements WireMockStub stubMappingIsValidWireMockStub(contractDsl) where: methodBuilderName | methodBuilder - "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodBodyBuilder(dsl) } + "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl) } "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } } @@ -432,7 +432,7 @@ class MockMvcMethodBodyBuilderSpec extends Specification implements WireMockStub stubMappingIsValidWireMockStub(contractDsl) where: methodBuilderName | methodBuilder - "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodBodyBuilder(dsl) } + "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl) } "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } } @@ -463,7 +463,7 @@ class MockMvcMethodBodyBuilderSpec extends Specification implements WireMockStub stubMappingIsValidWireMockStub(contractDsl) where: methodBuilderName | methodBuilder - "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodBodyBuilder(dsl) } + "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl) } "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } } @@ -511,7 +511,7 @@ class MockMvcMethodBodyBuilderSpec extends Specification implements WireMockStub stubMappingIsValidWireMockStub(contractDsl) where: methodBuilderName | methodBuilder - "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodBodyBuilder(dsl) } + "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl) } "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } } @@ -560,7 +560,7 @@ class MockMvcMethodBodyBuilderSpec extends Specification implements WireMockStub stubMappingIsValidWireMockStub(contractDsl) where: methodBuilderName | methodBuilder - "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodBodyBuilder(dsl) } + "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl) } "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } } @@ -587,9 +587,9 @@ class MockMvcMethodBodyBuilderSpec extends Specification implements WireMockStub and: stubMappingIsValidWireMockStub(contractDsl) where: - methodBuilderName | methodBuilder | bodyString - "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodBodyBuilder(dsl) } | ".body('''''')" - "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } | ".body(\"\\\"\\\"\")" + methodBuilderName | methodBuilder | bodyString + "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl) } | ".body('''''')" + "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } | ".body(\"\\\"\\\"\")" } @Unroll @@ -616,9 +616,9 @@ class MockMvcMethodBodyBuilderSpec extends Specification implements WireMockStub and: stubMappingIsValidWireMockStub(contractDsl) where: - methodBuilderName | methodBuilder | bodyDefinitionString | bodyEvaluationString - "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodBodyBuilder(dsl) } | 'def responseBody = (response.body.asString())' | 'responseBody == "test"' - "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } | 'Object responseBody = (response.getBody().asString());' | 'assertThat(responseBody).isEqualTo("test");' + methodBuilderName | methodBuilder | bodyDefinitionString | bodyEvaluationString + "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl) } | 'def responseBody = (response.body.asString())' | 'responseBody == "test"' + "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } | 'Object responseBody = (response.getBody().asString());' | 'assertThat(responseBody).isEqualTo("test");' } @Issue('113') @@ -656,9 +656,9 @@ class MockMvcMethodBodyBuilderSpec extends Specification implements WireMockStub and: stubMappingIsValidWireMockStub(contractDsl) where: - methodBuilderName | methodBuilder | headerEvaluationString - "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodBodyBuilder(dsl) } | '''response.header('Location') ==~ java.util.regex.Pattern.compile('http://localhost/partners/[0-9]+/users/[0-9]+')''' - "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } | 'assertThat(response.header("Location")).matches("http://localhost/partners/[0-9]+/users/[0-9]+");' + methodBuilderName | methodBuilder | headerEvaluationString + "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl) } | '''response.header('Location') ==~ java.util.regex.Pattern.compile('http://localhost/partners/[0-9]+/users/[0-9]+')''' + "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } | 'assertThat(response.header("Location")).matches("http://localhost/partners/[0-9]+/users/[0-9]+");' } @Issue('115') @@ -696,9 +696,9 @@ class MockMvcMethodBodyBuilderSpec extends Specification implements WireMockStub and: stubMappingIsValidWireMockStub(contractDsl) where: - methodBuilderName | methodBuilder | headerEvaluationString - "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodBodyBuilder(dsl) } | '''response.header('Location') ==~ java.util.regex.Pattern.compile('^((http[s]?|ftp):\\/)\\/?([^:\\/\\s]+)(:[0-9]{1,5})?/partners/[0-9]+/users/[0-9]+')''' - "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } | 'assertThat(response.header("Location")).matches("^((http[s]?|ftp):/)/?([^:/s]+)(:[0-9]{1,5})?/partners/[0-9]+/users/[0-9]+");' + methodBuilderName | methodBuilder | headerEvaluationString + "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl) } | '''response.header('Location') ==~ java.util.regex.Pattern.compile('^((http[s]?|ftp):\\/)\\/?([^:\\/\\s]+)(:[0-9]{1,5})?/partners/[0-9]+/users/[0-9]+')''' + "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } | 'assertThat(response.header("Location")).matches("^((http[s]?|ftp):/)/?([^:/s]+)(:[0-9]{1,5})?/partners/[0-9]+/users/[0-9]+");' } @Unroll @@ -739,7 +739,7 @@ class MockMvcMethodBodyBuilderSpec extends Specification implements WireMockStub stubMappingIsValidWireMockStub(contractDsl) where: methodBuilderName | methodBuilder - "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodBodyBuilder(dsl) } + "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl) } "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } } @@ -773,7 +773,7 @@ class MockMvcMethodBodyBuilderSpec extends Specification implements WireMockStub stubMappingIsValidWireMockStub(contractDsl) where: methodBuilderName | methodBuilder - "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodBodyBuilder(dsl) } + "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl) } "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } } @@ -813,7 +813,7 @@ class MockMvcMethodBodyBuilderSpec extends Specification implements WireMockStub test.contains("""assertThatJson(parsedJson).field("message").matches("User not found by email = \\\\\\\\[[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\\\\\.[a-zA-Z]{2,4}\\\\\\\\]")""") where: methodBuilderName | methodBuilder - "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodBodyBuilder(dsl) } + "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl) } "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } } @@ -821,7 +821,7 @@ class MockMvcMethodBodyBuilderSpec extends Specification implements WireMockStub @Unroll def "should not omit the optional field in the test creation with MockMvcSpockMethodBodyBuilder"() { given: - MethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl) + MethodBodyBuilder builder = new MockMvcSpockMethodRequestProcessingBodyBuilder(contractDsl) BlockBuilder blockBuilder = new BlockBuilder(" ") when: builder.appendTo(blockBuilder) @@ -900,9 +900,9 @@ class MockMvcMethodBodyBuilderSpec extends Specification implements WireMockStub assert test.contains(assertionString) } where: - methodBuilderName | methodBuilder | assertionStrings - "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodBodyBuilder(dsl) } | ['''assertThatRejectionReasonIsNull(parsedJson.read('$.rejectionReason'))''', '''assertThatLocationIsNull(response.header('Location'))'''] - "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } | ['''assertThatRejectionReasonIsNull(parsedJson.read("$.rejectionReason"))''', '''assertThatLocationIsNull(response.header("Location"))'''] + methodBuilderName | methodBuilder | assertionStrings + "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl) } | ['''assertThatRejectionReasonIsNull(parsedJson.read('$.rejectionReason'))''', '''assertThatLocationIsNull(response.header('Location'))'''] + "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } | ['''assertThatRejectionReasonIsNull(parsedJson.read("$.rejectionReason"))''', '''assertThatLocationIsNull(response.header("Location"))'''] } @Unroll @@ -968,9 +968,9 @@ class MockMvcMethodBodyBuilderSpec extends Specification implements WireMockStub !test.contains("clientValue") !test.contains("cursor") where: - methodBuilderName | methodBuilder | bodyString - "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodBodyBuilder(dsl) } | '"street":"Light Street"' - "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } | '\\"street\\":\\"Light Street\\"' + methodBuilderName | methodBuilder | bodyString + "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl) } | '"street":"Light Street"' + "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } | '\\"street\\":\\"Light Street\\"' } @@ -1009,7 +1009,7 @@ class MockMvcMethodBodyBuilderSpec extends Specification implements WireMockStub !test.contains("\\u041f") where: methodBuilderName | methodBuilder - "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodBodyBuilder(dsl) } + "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl) } "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } } @@ -1036,10 +1036,10 @@ World.''') then: test.contains(bodyString) where: - methodBuilderName | methodBuilder | bodyString - "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodBodyBuilder(dsl) } | """'''hello, + methodBuilderName | methodBuilder | bodyString + "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl) } | """'''hello, World.'''""" - "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } | '\\"hello,\\nWorld.\\"' + "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } | '\\"hello,\\nWorld.\\"' } @Issue('180') @@ -1073,12 +1073,12 @@ World.'''""" test.contains(requestString) } where: - methodBuilderName | methodBuilder | requestStrings - "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodBodyBuilder(dsl) } | ["""'content-type', 'multipart/form-data;boundary=AaB03x'""", - """.param('formParameter', '"formParameterValue"'""", - """.param('someBooleanParameter', 'true')""", - """.multiPart('file', 'filename.csv', 'file content'.bytes)"""] - "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } | ['"content-type", "multipart/form-data;boundary=AaB03x"', + methodBuilderName | methodBuilder | requestStrings + "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl) } | ["""'content-type', 'multipart/form-data;boundary=AaB03x'""", + """.param('formParameter', '"formParameterValue"'""", + """.param('someBooleanParameter', 'true')""", + """.multiPart('file', 'filename.csv', 'file content'.bytes)"""] + "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } | ['"content-type", "multipart/form-data;boundary=AaB03x"', '.param("formParameter", "\\"formParameterValue\\"")', '.param("someBooleanParameter", "true")', '.multiPart("file", "filename.csv", "file content".getBytes());'] @@ -1113,7 +1113,7 @@ World.'''""" test.contains('.multiPart') where: methodBuilderName | methodBuilder - "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodBodyBuilder(dsl) } + "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl) } "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } } @@ -1152,7 +1152,7 @@ World.'''""" test.contains('''assertThatJson(parsedJson).array("authorities").matches("^[a-zA-Z0-9_\\\\- ]+\\$").value()''') where: methodBuilderName | methodBuilder - "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodBodyBuilder(dsl) } + "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl) } "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } } @@ -1183,7 +1183,7 @@ World.'''""" test.contains('''assertThatRejectionReasonIsNull(''') where: methodBuilderName | methodBuilder - "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodBodyBuilder(dsl) } + "MockMvcSpockMethodBuilder" | { GroovyDsl dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl) } "MockMvcJUnitMethodBuilder" | { GroovyDsl dsl -> new MockMvcJUnitMethodBodyBuilder(dsl) } } diff --git a/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/AccurestGradlePlugin.groovy b/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/AccurestGradlePlugin.groovy index 6288fa93b4..ceb090dd99 100644 --- a/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/AccurestGradlePlugin.groovy +++ b/accurest-gradle-plugin/src/main/groovy/io/codearte/accurest/plugin/AccurestGradlePlugin.groovy @@ -31,6 +31,7 @@ class AccurestGradlePlugin implements Plugin { project.dependencies.add("testCompile", "com.github.tomakehurst:wiremock:2.0.5-beta") project.dependencies.add("testCompile", "com.toomuchcoding.jsonassert:jsonassert:${extension.getJsonAssertVersion()}") project.dependencies.add("testCompile", "org.assertj:assertj-core:2.3.0") + project.dependencies.add("testCompile", "io.codearte.accurest:accurest-messaging-core:${extension.getAccurestMessagingCoreVersion()}") project.afterEvaluate { def hasIdea = project.plugins.findPlugin(IDEA_PLUGIN_CLASS) diff --git a/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/AccurestIntegrationSpec.groovy b/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/AccurestIntegrationSpec.groovy index ffcfe16442..1bddae8b6d 100644 --- a/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/AccurestIntegrationSpec.groovy +++ b/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/AccurestIntegrationSpec.groovy @@ -24,9 +24,13 @@ class AccurestIntegrationSpec extends IntegrationSpec { } protected void switchToJunitTestFramework() { + switchToJunitTestFramework(MVC_SPEC, MVC_TEST) + } + + protected void switchToJunitTestFramework(String from, String to) { Path path = buildFile.toPath() String content = new StringBuilder(new String(Files.readAllBytes(path), UTF_8)).replaceAll(SPOCK, JUNIT) - .replaceAll(MVC_SPEC, MVC_TEST) + .replaceAll(from, to) Files.write(path, content.getBytes(UTF_8)) } diff --git a/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/MessagingProjectSpec.groovy b/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/MessagingProjectSpec.groovy new file mode 100755 index 0000000000..df468fdfa7 --- /dev/null +++ b/accurest-gradle-plugin/src/test/groovy/io/codearte/accurest/plugin/MessagingProjectSpec.groovy @@ -0,0 +1,29 @@ +package io.codearte.accurest.plugin + +import spock.lang.Stepwise + +@Stepwise +class MessagingProjectSpec extends AccurestIntegrationSpec { + + void setup() { + copyResources("functionalTest/messagingProject", "") + runTasksSuccessfully('clean') //delete accidental output when previously importing SimpleBoot into Idea to tweak it + } + + def "should pass basic flow for Spock"() { + given: + assert fileExists('build.gradle') + expect: + runTasksSuccessfully('check') + } + + def "should pass basic flow for JUnit"() { + given: + runTasksSuccessfully('clean') + assert fileExists('build.gradle') + expect: + switchToJunitTestFramework('io.codearte.accurest.samples.book.MessagingBaseSpec', 'io.codearte.accurest.samples.book.MessagingBaseTest') + runTasksSuccessfully('check') + } + +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/build.gradle b/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/build.gradle new file mode 100644 index 0000000000..824eb68f2f --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/build.gradle @@ -0,0 +1,105 @@ +buildscript { + repositories { + mavenLocal() + mavenCentral() + } + dependencies { + // will be passed via classpath + // classpath "io.codearte.accurest:accurest-gradle-plugin:+" + } +} + +apply plugin: 'groovy' +apply plugin: 'accurest' +apply plugin: 'maven-publish' + +ext { + contractsDir = file("${project.rootDir}/repository/mappings/") + stubsOutputDirRoot = file("${project.buildDir}/production/${project.name}-stubs/") + wireMockStubsOutputDir = new File(stubsOutputDirRoot, 'repository/mappings/') + contractsOutputDir = new File(stubsOutputDirRoot, 'repository/accurest/') +} + +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}") + } + } + } + } +} + +repositories { + mavenCentral() + mavenLocal() + maven { + url "http://repo.spring.io/snapshot" + } + maven { + url "http://repo.spring.io/milestone" + } +} + +dependencies { + compile "org.codehaus.groovy:groovy-all:2.4.5" + compile 'org.springframework.boot:spring-boot-starter-web:1.3.3.RELEASE' + compile 'org.springframework.boot:spring-boot-starter-actuator:1.3.3.RELEASE' + compile 'org.springframework.boot:spring-boot-starter-integration:1.3.3.RELEASE' + // will be passed via classpath (I have issues with doing that quite frankly) + compile 'io.codearte.accurest:accurest-messaging-integration:+' + + testCompile "org.spockframework:spock-spring:1.0-groovy-2.4" + testCompile 'org.springframework.boot:spring-boot-starter-test:1.3.3.RELEASE' + testCompile "ch.qos.logback:logback-classic:1.1.2" +} + +accurest { + //baseClassForTests = 'io.codearte.accurest.samples.book.MessagingBaseTest' + baseClassForTests = 'io.codearte.accurest.samples.book.MessagingBaseSpec' + basePackageForTests = 'accurest' + //targetFramework = 'JUnit' + targetFramework = 'Spock' + contractsDslDir = contractsDir +// generatedTestSourcesDir = file("${project.rootDir}/src/test/groovy/") + stubsOutputDir = wireMockStubsOutputDir +} + +//TODO: Put it into the plugin +task createWireMockStubsOutputDir << { + wireMockStubsOutputDir.mkdirs() +} + +generateWireMockClientStubs.dependsOn { createWireMockStubsOutputDir } +generateAccurest.dependsOn generateWireMockClientStubs + +wrapper { + gradleVersion '2.12' +} + +task copyContracts(type: Copy) { + from contractsDir + into contractsOutputDir +} + +task stubsJar(type: Jar, dependsOn: ["generateWireMockClientStubs", copyContracts]) { + baseName = "${project.name}-stubs" + from stubsOutputDirRoot +} + +artifacts { + archives stubsJar +} + +publishing { + publications { + stubs(MavenPublication) { + artifactId "${project.name}-stubs" + artifact stubsJar + } + } +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/gradle.properties b/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/gradle.properties new file mode 100644 index 0000000000..47ede0913e --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/gradle.properties @@ -0,0 +1,4 @@ +groupId=io.codearte +jacksonMapper=1.9.13 +restAssuredVersion=2.9.0 +springVersion=4.2.3.RELEASE \ No newline at end of file diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/gradle/wrapper/gradle-wrapper.jar b/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/gradle/wrapper/gradle-wrapper.jar new file mode 100755 index 0000000000..2c6137b878 Binary files /dev/null and b/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/gradle/wrapper/gradle-wrapper.jar differ diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/gradle/wrapper/gradle-wrapper.properties b/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/gradle/wrapper/gradle-wrapper.properties new file mode 100755 index 0000000000..a1f7bb3d56 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sun Apr 17 20:31:11 CEST 2016 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.12-bin.zip diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/gradlew b/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/gradlew new file mode 100755 index 0000000000..9d82f78915 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/gradlew @@ -0,0 +1,160 @@ +#!/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 + +# 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\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +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"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/gradlew.bat b/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/gradlew.bat new file mode 100755 index 0000000000..5f192121eb --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/repository/mappings/bookDeleted.groovy b/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/repository/mappings/bookDeleted.groovy new file mode 100644 index 0000000000..aa319831a4 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/repository/mappings/bookDeleted.groovy @@ -0,0 +1,13 @@ +io.codearte.accurest.dsl.GroovyDsl.make { + label 'some_label' + input { + messageFrom('delete') + messageBody([ + bookName: 'foo' + ]) + messageHeaders { + header('sample', 'header') + } + assertThat('bookWasDeleted()') + } +} \ No newline at end of file diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/repository/mappings/bookReturned1.groovy b/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/repository/mappings/bookReturned1.groovy new file mode 100644 index 0000000000..b21b1c7cd1 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/repository/mappings/bookReturned1.groovy @@ -0,0 +1,13 @@ +io.codearte.accurest.dsl.GroovyDsl.make { + label 'some_label' + input { + triggeredBy('bookReturnedTriggered()') + } + outputMessage { + sentTo('output') + body('''{ "bookName" : "foo" }''') + headers { + header('BOOK-NAME', 'foo') + } + } +} \ No newline at end of file diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/repository/mappings/bookReturned2.groovy b/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/repository/mappings/bookReturned2.groovy new file mode 100644 index 0000000000..e6090df410 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/repository/mappings/bookReturned2.groovy @@ -0,0 +1,21 @@ +io.codearte.accurest.dsl.GroovyDsl.make { + label 'some_label' + input { + messageFrom('input') + messageBody([ + bookName: 'foo' + ]) + messageHeaders { + header('sample', 'header') + } + } + outputMessage { + sentTo('output') + body([ + bookName: 'foo' + ]) + headers { + header('BOOK-NAME', 'foo') + } + } +} \ No newline at end of file diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/settings.gradle b/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/settings.gradle new file mode 100644 index 0000000000..d227b869a2 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/settings.gradle @@ -0,0 +1 @@ +rootProject.name='bootSimple' diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/src/main/groovy/io/codearte/accurest/samples/book/BookDeleted.groovy b/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/src/main/groovy/io/codearte/accurest/samples/book/BookDeleted.groovy new file mode 100644 index 0000000000..bf3cd9b68c --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/src/main/groovy/io/codearte/accurest/samples/book/BookDeleted.groovy @@ -0,0 +1,14 @@ +package io.codearte.accurest.samples.book + +import com.fasterxml.jackson.annotation.JsonCreator +import groovy.transform.CompileStatic + +@CompileStatic +class BookDeleted { + final String bookName + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + BookDeleted(String bookName) { + this.bookName = bookName + } +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/src/main/groovy/io/codearte/accurest/samples/book/BookListener.groovy b/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/src/main/groovy/io/codearte/accurest/samples/book/BookListener.groovy new file mode 100644 index 0000000000..60838893b8 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/src/main/groovy/io/codearte/accurest/samples/book/BookListener.groovy @@ -0,0 +1,38 @@ +package io.codearte.accurest.samples.book + +import groovy.transform.CompileStatic +import groovy.util.logging.Slf4j +import org.springframework.messaging.Message +import org.springframework.messaging.MessageHeaders +import org.springframework.messaging.support.MessageBuilder + +import java.util.concurrent.atomic.AtomicBoolean + +@CompileStatic +@Slf4j +class BookListener { + + /** + Scenario for "should generate tests triggered by a message": + client side: if sends a message to input.messageFrom then message will be sent to output.messageFrom + server side: will send a message to input, verify the message contents and await upon receiving message on the output messageFrom + */ + Message returnBook(BookReturned bookReturned) { + log.info("Returning book [$bookReturned]") + return MessageBuilder.createMessage(bookReturned, new MessageHeaders([ + 'BOOK-NAME': bookReturned.bookName as Object + ])) + } + + /** + Scenario for "should generate tests triggered by a message": + client side: if sends a message to input.messageFrom then message will be sent to output.messageFrom + server side: will send a message to input, verify the message contents and await upon receiving message on the output messageFrom + */ + void bookDeleted(BookDeleted bookDeleted) { + log.info("Deleting book [$bookDeleted]") + bookSuccessfulyDeleted.set(true) + } + + AtomicBoolean bookSuccessfulyDeleted = new AtomicBoolean(false) +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/src/main/groovy/io/codearte/accurest/samples/book/BookReturned.groovy b/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/src/main/groovy/io/codearte/accurest/samples/book/BookReturned.groovy new file mode 100644 index 0000000000..3b244a0395 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/src/main/groovy/io/codearte/accurest/samples/book/BookReturned.groovy @@ -0,0 +1,14 @@ +package io.codearte.accurest.samples.book + +import com.fasterxml.jackson.annotation.JsonCreator +import groovy.transform.CompileStatic + +@CompileStatic +class BookReturned { + final String bookName + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + BookReturned(String bookName) { + this.bookName = bookName + } +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/src/main/groovy/io/codearte/accurest/samples/book/BookService.groovy b/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/src/main/groovy/io/codearte/accurest/samples/book/BookService.groovy new file mode 100644 index 0000000000..25e943fc93 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/src/main/groovy/io/codearte/accurest/samples/book/BookService.groovy @@ -0,0 +1,32 @@ +package io.codearte.accurest.samples.book + +import groovy.transform.CompileStatic +import groovy.util.logging.Slf4j +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.messaging.MessageChannel +import org.springframework.messaging.MessageHeaders +import org.springframework.messaging.support.MessageBuilder +import org.springframework.stereotype.Service + +@Service +@CompileStatic +@Slf4j +class BookService { + + @Autowired @Qualifier("outputChannel") MessageChannel outputChannel + + /** + Scenario for "should generate tests triggered by a method": + client side: must have a possibility to "trigger" sending of a message to the given messageFrom + server side: will run the method and await upon receiving message on the output messageFrom + + Method triggers sending a message to a source + */ + void returnBook(BookReturned bookReturned) { + log.info("Returning book [$bookReturned]") + outputChannel.send(MessageBuilder.createMessage(bookReturned, new MessageHeaders([ + 'BOOK-NAME': bookReturned.bookName as Object + ]))) + } +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/src/main/groovy/io/codearte/accurest/samples/book/IntegrationMessagingApplication.groovy b/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/src/main/groovy/io/codearte/accurest/samples/book/IntegrationMessagingApplication.groovy new file mode 100644 index 0000000000..820ec8f52e --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/src/main/groovy/io/codearte/accurest/samples/book/IntegrationMessagingApplication.groovy @@ -0,0 +1,14 @@ +package io.codearte.accurest.samples.book + +import org.springframework.boot.SpringApplication +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.context.annotation.ImportResource + +@SpringBootApplication +@ImportResource("classpath*:integration-context.xml") +class IntegrationMessagingApplication { + + static void main(String[] args) { + SpringApplication.run(IntegrationMessagingApplication.class, args) + } +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/src/main/resources/integration-context.xml b/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/src/main/resources/integration-context.xml new file mode 100644 index 0000000000..e8c7accb04 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/src/main/resources/integration-context.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/src/test/groovy/io/codearte/accurest/samples/book/MessagingBaseSpec.groovy b/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/src/test/groovy/io/codearte/accurest/samples/book/MessagingBaseSpec.groovy new file mode 100644 index 0000000000..8d25db00f6 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/src/test/groovy/io/codearte/accurest/samples/book/MessagingBaseSpec.groovy @@ -0,0 +1,25 @@ +package io.codearte.accurest.samples.book + +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.SpringApplicationContextLoader +import org.springframework.test.context.ContextConfiguration +import spock.lang.Specification +/** + * @author Marcin Grzejszczak + */ +@ContextConfiguration(classes = [IntegrationMessagingApplication], loader = SpringApplicationContextLoader) +abstract class MessagingBaseSpec extends Specification { + + // BASE CLASS WOULD HAVE THIS: + + @Autowired BookService bookService + @Autowired BookListener bookListener + + void bookReturnedTriggered() { + bookService.returnBook(new BookReturned("foo")) + } + + void bookWasDeleted() { + assert bookListener.bookSuccessfulyDeleted.get() + } +} diff --git a/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/src/test/java/io/codearte/accurest/samples/book/MessagingBaseTest.java b/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/src/test/java/io/codearte/accurest/samples/book/MessagingBaseTest.java new file mode 100644 index 0000000000..c5ee6bf196 --- /dev/null +++ b/accurest-gradle-plugin/src/test/resources/functionalTest/messagingProject/src/test/java/io/codearte/accurest/samples/book/MessagingBaseTest.java @@ -0,0 +1,29 @@ +package io.codearte.accurest.samples.book; + +import org.assertj.core.api.Assertions; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.SpringApplicationContextLoader; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @author Marcin Grzejszczak + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = {IntegrationMessagingApplication.class}, loader = SpringApplicationContextLoader.class) +public abstract class MessagingBaseTest { + + // BASE CLASS WOULD HAVE THIS: + + @Autowired BookService bookService; + @Autowired BookListener bookListener; + + public void bookReturnedTriggered() { + bookService.returnBook(new BookReturned("foo")); + } + + public void bookWasDeleted() { + Assertions.assertThat(bookListener.getBookSuccessfulyDeleted().get()).isTrue(); + } +} diff --git a/accurest-messaging/accurest-messaging-camel/build.gradle b/accurest-messaging/accurest-messaging-camel/build.gradle new file mode 100644 index 0000000000..ed24c577eb --- /dev/null +++ b/accurest-messaging/accurest-messaging-camel/build.gradle @@ -0,0 +1,16 @@ +repositories { + mavenLocal() + jcenter() + maven { + url "http://repo.spring.io/snapshot" + } + maven { + url "http://repo.spring.io/milestone" + } +} + +dependencies { + compile project(':accurest-messaging-root:accurest-messaging-core') + compile 'org.apache.camel:camel-spring:[2.9.0,)' + compile 'org.slf4j:slf4j-api:[1.6.0,)' +} \ No newline at end of file diff --git a/accurest-messaging/accurest-messaging-camel/src/main/java/io/codearte/accurest/messaging/camel/AccurestCamelConfiguration.java b/accurest-messaging/accurest-messaging-camel/src/main/java/io/codearte/accurest/messaging/camel/AccurestCamelConfiguration.java new file mode 100644 index 0000000000..b96caf5ccc --- /dev/null +++ b/accurest-messaging/accurest-messaging-camel/src/main/java/io/codearte/accurest/messaging/camel/AccurestCamelConfiguration.java @@ -0,0 +1,22 @@ +package io.codearte.accurest.messaging.camel; + +import io.codearte.accurest.messaging.AccurestMessageBuilder; +import io.codearte.accurest.messaging.AccurestMessaging; +import org.apache.camel.CamelContext; +import org.springframework.context.annotation.Bean; + +/** + * @author Marcin Grzejszczak + */ +public class AccurestCamelConfiguration { + + @Bean + AccurestMessaging accurestMessaging(CamelContext context, AccurestMessageBuilder builder) { + return new AccurestCamelMessaging(context, builder); + } + + @Bean + AccurestMessageBuilder accurestMessageBuilder() { + return new AccurestCamelMessageBuilder(); + } +} diff --git a/accurest-messaging/accurest-messaging-camel/src/main/java/io/codearte/accurest/messaging/camel/AccurestCamelMessageBuilder.java b/accurest-messaging/accurest-messaging-camel/src/main/java/io/codearte/accurest/messaging/camel/AccurestCamelMessageBuilder.java new file mode 100644 index 0000000000..94b808d8b5 --- /dev/null +++ b/accurest-messaging/accurest-messaging-camel/src/main/java/io/codearte/accurest/messaging/camel/AccurestCamelMessageBuilder.java @@ -0,0 +1,27 @@ +package io.codearte.accurest.messaging.camel; + +import io.codearte.accurest.messaging.AccurestMessage; +import io.codearte.accurest.messaging.AccurestMessageBuilder; +import org.apache.camel.Message; +import org.apache.camel.impl.DefaultMessage; + +import java.util.Map; + +/** + * @author Marcin Grzejszczak + */ +public class AccurestCamelMessageBuilder implements AccurestMessageBuilder { + + @Override + public AccurestMessage create(T payload, Map headers) { + DefaultMessage message = new DefaultMessage(); + message.setBody(payload); + message.setHeaders(headers); + return new CamelMessage<>(message); + } + + @Override + public AccurestMessage create(Message message) { + return new CamelMessage<>(message); + } +} diff --git a/accurest-messaging/accurest-messaging-camel/src/main/java/io/codearte/accurest/messaging/camel/AccurestCamelMessaging.java b/accurest-messaging/accurest-messaging-camel/src/main/java/io/codearte/accurest/messaging/camel/AccurestCamelMessaging.java new file mode 100644 index 0000000000..bf1eef7dc1 --- /dev/null +++ b/accurest-messaging/accurest-messaging-camel/src/main/java/io/codearte/accurest/messaging/camel/AccurestCamelMessaging.java @@ -0,0 +1,82 @@ +package io.codearte.accurest.messaging.camel; + +import io.codearte.accurest.messaging.AccurestMessage; +import io.codearte.accurest.messaging.AccurestMessageBuilder; +import io.codearte.accurest.messaging.AccurestMessaging; +import org.apache.camel.CamelContext; +import org.apache.camel.ConsumerTemplate; +import org.apache.camel.Exchange; +import org.apache.camel.Message; +import org.apache.camel.ProducerTemplate; +import org.apache.camel.impl.DefaultExchange; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * @author Marcin Grzejszczak + */ +@Component +public class AccurestCamelMessaging implements AccurestMessaging { + + private static final Logger log = LoggerFactory.getLogger(AccurestCamelMessaging.class); + + private final CamelContext context; + private final AccurestMessageBuilder builder; + + @Autowired + @SuppressWarnings("unchecked") + public AccurestCamelMessaging(CamelContext context, AccurestMessageBuilder accurestMessageBuilder) { + this.context = context; + this.builder = accurestMessageBuilder; + } + + @Override + public void send(AccurestMessage message, String destination) { + try { + ProducerTemplate producerTemplate = context.createProducerTemplate(); + Exchange exchange = new DefaultExchange(context); + exchange.setIn(message.convert()); + producerTemplate.send(destination, exchange); + } catch (Exception e) { + log.error("Exception occurred while trying to send a message [" + message + "] " + + "to a channel with name [" + destination + "]", e); + throw e; + } + } + + @Override + @SuppressWarnings("unchecked") + public AccurestMessage receiveMessage(String destination, long timeout, TimeUnit timeUnit) { + try { + ConsumerTemplate consumerTemplate = context.createConsumerTemplate(); + Exchange exchange = consumerTemplate.receive(destination, timeUnit.toMillis(timeout)); + return builder.create(exchange.getIn()); + } catch (Exception e) { + log.error("Exception occurred while trying to read a message from " + + " a channel with name [" + destination + "]", e); + throw new RuntimeException(e); + } + } + + @Override + public AccurestMessage receiveMessage(String destination) { + return receiveMessage(destination, 5, TimeUnit.SECONDS); + } + + @Override + @SuppressWarnings("unchecked") + public AccurestMessage create(T t, Map headers) { + return builder.create(t, headers); + } + + @Override + @SuppressWarnings("unchecked") + public AccurestMessage create(Message message) { + return builder.create(message); + } +} diff --git a/accurest-messaging/accurest-messaging-camel/src/main/java/io/codearte/accurest/messaging/camel/CamelMessage.java b/accurest-messaging/accurest-messaging-camel/src/main/java/io/codearte/accurest/messaging/camel/CamelMessage.java new file mode 100644 index 0000000000..6511617a89 --- /dev/null +++ b/accurest-messaging/accurest-messaging-camel/src/main/java/io/codearte/accurest/messaging/camel/CamelMessage.java @@ -0,0 +1,40 @@ +package io.codearte.accurest.messaging.camel; + +import io.codearte.accurest.messaging.AccurestMessage; +import org.apache.camel.Message; + +import java.util.Map; + +/** + * @author Marcin Grzejszczak + */ +public class CamelMessage implements AccurestMessage { + + private final Message delegate; + + public CamelMessage(Message delegate) { + this.delegate = delegate; + } + + @Override + @SuppressWarnings("unchecked") + public T getPayload() { + return (T) delegate.getBody(); + } + + @Override + public Map getHeaders() { + return delegate.getHeaders(); + } + + @Override + public Object getHeader(String key) { + return getHeaders().get(key); + } + + @Override + public Message convert() { + return delegate; + } + +} diff --git a/accurest-messaging/accurest-messaging-camel/src/main/resources/META-INF/spring.factories b/accurest-messaging/accurest-messaging-camel/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000000..318eec3472 --- /dev/null +++ b/accurest-messaging/accurest-messaging-camel/src/main/resources/META-INF/spring.factories @@ -0,0 +1,3 @@ +# Auto Configuration +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +io.codearte.accurest.messaging.camel.AccurestCamelConfiguration diff --git a/accurest-messaging/accurest-messaging-core/build.gradle b/accurest-messaging/accurest-messaging-core/build.gradle new file mode 100644 index 0000000000..f63ffbeb9b --- /dev/null +++ b/accurest-messaging/accurest-messaging-core/build.gradle @@ -0,0 +1,8 @@ +repositories { + jcenter() +} + +dependencies { + compile 'com.fasterxml.jackson.core:jackson-databind:[2.4.4,)' + compile 'javax.inject:javax.inject:1' +} \ No newline at end of file diff --git a/accurest-messaging/accurest-messaging-core/src/main/java/io/codearte/accurest/messaging/AccurestMessage.java b/accurest-messaging/accurest-messaging-core/src/main/java/io/codearte/accurest/messaging/AccurestMessage.java new file mode 100644 index 0000000000..65710a537a --- /dev/null +++ b/accurest-messaging/accurest-messaging-core/src/main/java/io/codearte/accurest/messaging/AccurestMessage.java @@ -0,0 +1,32 @@ +package io.codearte.accurest.messaging; + +import java.util.Map; + +/** + * Describes a message. Contains payload and headers. A message can be converted + * to another type (e.g. Spring Messaging Message) + * + * @author Marcin Grzejszczak + */ +public interface AccurestMessage { + + /** + * Returns a payload of type {@code PAYLOAD} + */ + PAYLOAD getPayload(); + + /** + * Returns a map of headers + */ + Map getHeaders(); + + /** + * Returns a header for a given key + */ + Object getHeader(String key); + + /** + * Converts the message to {@code TYPE_TO_CONVERT_INTO} type + */ + TYPE_TO_CONVERT_INTO convert(); +} diff --git a/accurest-messaging/accurest-messaging-core/src/main/java/io/codearte/accurest/messaging/AccurestMessageBuilder.java b/accurest-messaging/accurest-messaging-core/src/main/java/io/codearte/accurest/messaging/AccurestMessageBuilder.java new file mode 100644 index 0000000000..3d0a0e37e4 --- /dev/null +++ b/accurest-messaging/accurest-messaging-core/src/main/java/io/codearte/accurest/messaging/AccurestMessageBuilder.java @@ -0,0 +1,22 @@ +package io.codearte.accurest.messaging; + +import java.util.Map; + +/** + * Contract for creation of (@link AccurestMessage}. You can create a message from + * payload and headers or from some type (e.g. Spring Messaging Message). + * + * @author Marcin Grzejszczak + */ +public interface AccurestMessageBuilder { + + /** + * Creates a {@link AccurestMessage} from payload and headers + */ + AccurestMessage create(PAYLOAD payload, Map headers); + + /** + * Creates a {@link AccurestMessage} from the {@code TYPE_TO_CONVERT_INTO} type + */ + AccurestMessage create(TYPE_TO_CONVERT_INTO typeToConvertInto); +} diff --git a/accurest-messaging/accurest-messaging-core/src/main/java/io/codearte/accurest/messaging/AccurestMessaging.java b/accurest-messaging/accurest-messaging-core/src/main/java/io/codearte/accurest/messaging/AccurestMessaging.java new file mode 100644 index 0000000000..226f800046 --- /dev/null +++ b/accurest-messaging/accurest-messaging-core/src/main/java/io/codearte/accurest/messaging/AccurestMessaging.java @@ -0,0 +1,28 @@ +package io.codearte.accurest.messaging; + +import java.util.concurrent.TimeUnit; + +/** + * Core interface that allows you to build, send and receive messages. + * + * Destination is relevant to the underlaying implementation. Might be a channel, queue, topic etc. + * + * @author Marcin Grzejszczak + */ +public interface AccurestMessaging extends AccurestMessageBuilder { + /** + * Sends the {@link AccurestMessage} to the given destination. + */ + void send(AccurestMessage message, String destination); + + /** + * Receives the {@link AccurestMessage} from the given destination. You can provide the timeout + * for receiving that message. + */ + AccurestMessage receiveMessage(String destination, long timeout, TimeUnit timeUnit); + + /** + * Receives the {@link AccurestMessage} from the given destination. A default timeout will be applied. + */ + AccurestMessage receiveMessage(String destination); +} diff --git a/accurest-messaging/accurest-messaging-core/src/main/java/io/codearte/accurest/messaging/AccurestMessagingUtil.java b/accurest-messaging/accurest-messaging-core/src/main/java/io/codearte/accurest/messaging/AccurestMessagingUtil.java new file mode 100644 index 0000000000..d2749c03ea --- /dev/null +++ b/accurest-messaging/accurest-messaging-core/src/main/java/io/codearte/accurest/messaging/AccurestMessagingUtil.java @@ -0,0 +1,98 @@ +package io.codearte.accurest.messaging; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * Helper class that allows to build headers in a nice way + * + * @author Marcin Grzejszczak + */ +public class AccurestMessagingUtil { + + public static AccurestHeaders headers() { + return new AccurestHeaders(); + } + + public static class AccurestHeaders implements Map { + + private final Map delegate = new HashMap<>(); + + public AccurestHeaders header(String key, Object value) { + put(key, value); + return this; + } + + @Override + public int size() { + return delegate.size(); + } + + @Override + public boolean isEmpty() { + return delegate.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return delegate.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return delegate.containsValue(value); + } + + @Override + public Object get(Object key) { + return delegate.get(key); + } + + @Override + public Object put(String key, Object value) { + return delegate.put(key, value); + } + + @Override + public Object remove(Object key) { + return delegate.remove(key); + } + + @Override + public void putAll(Map m) { + delegate.putAll(m); + } + + @Override + public void clear() { + delegate.clear(); + } + + @Override + public Set keySet() { + return delegate.keySet(); + } + + @Override + public Collection values() { + return delegate.values(); + } + + @Override + public Set> entrySet() { + return delegate.entrySet(); + } + + @Override + public boolean equals(Object o) { + return delegate.equals(o); + } + + @Override + public int hashCode() { + return delegate.hashCode(); + } + } +} diff --git a/accurest-messaging/accurest-messaging-core/src/main/java/io/codearte/accurest/messaging/noop/NoOpAccurestMessage.java b/accurest-messaging/accurest-messaging-core/src/main/java/io/codearte/accurest/messaging/noop/NoOpAccurestMessage.java new file mode 100644 index 0000000000..179a63d9b7 --- /dev/null +++ b/accurest-messaging/accurest-messaging-core/src/main/java/io/codearte/accurest/messaging/noop/NoOpAccurestMessage.java @@ -0,0 +1,30 @@ +package io.codearte.accurest.messaging.noop; + +import io.codearte.accurest.messaging.AccurestMessage; + +import java.util.Map; + +/** + * @author Marcin Grzejszczak + */ +public class NoOpAccurestMessage implements AccurestMessage { + @Override + public Object getPayload() { + return null; + } + + @Override + public Map getHeaders() { + return null; + } + + @Override + public Object getHeader(String key) { + return null; + } + + @Override + public Object convert() { + return null; + } +} diff --git a/accurest-messaging/accurest-messaging-core/src/main/java/io/codearte/accurest/messaging/noop/NoOpAccurestMessageBuilder.java b/accurest-messaging/accurest-messaging-core/src/main/java/io/codearte/accurest/messaging/noop/NoOpAccurestMessageBuilder.java new file mode 100644 index 0000000000..50cb34fffa --- /dev/null +++ b/accurest-messaging/accurest-messaging-core/src/main/java/io/codearte/accurest/messaging/noop/NoOpAccurestMessageBuilder.java @@ -0,0 +1,21 @@ +package io.codearte.accurest.messaging.noop; + +import io.codearte.accurest.messaging.AccurestMessage; +import io.codearte.accurest.messaging.AccurestMessageBuilder; + +import java.util.Map; + +/** + * @author Marcin Grzejszczak + */ +public class NoOpAccurestMessageBuilder implements AccurestMessageBuilder { + @Override + public AccurestMessage create(Object o, Map headers) { + return null; + } + + @Override + public AccurestMessage create(Object o) { + return null; + } +} diff --git a/accurest-messaging/accurest-messaging-core/src/main/java/io/codearte/accurest/messaging/noop/NoOpAccurestMessaging.java b/accurest-messaging/accurest-messaging-core/src/main/java/io/codearte/accurest/messaging/noop/NoOpAccurestMessaging.java new file mode 100644 index 0000000000..b99efe7a49 --- /dev/null +++ b/accurest-messaging/accurest-messaging-core/src/main/java/io/codearte/accurest/messaging/noop/NoOpAccurestMessaging.java @@ -0,0 +1,37 @@ +package io.codearte.accurest.messaging.noop; + +import io.codearte.accurest.messaging.AccurestMessage; +import io.codearte.accurest.messaging.AccurestMessaging; + +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * @author Marcin Grzejszczak + */ +public class NoOpAccurestMessaging implements AccurestMessaging { + @Override + public void send(AccurestMessage message, String destination) { + + } + + @Override + public AccurestMessage receiveMessage(String destination, long timeout, TimeUnit timeUnit) { + return null; + } + + @Override + public AccurestMessage receiveMessage(String destination) { + return null; + } + + @Override + public AccurestMessage create(Object o, Map headers) { + return null; + } + + @Override + public AccurestMessage create(Object o) { + return null; + } +} diff --git a/accurest-messaging/accurest-messaging-integration/build.gradle b/accurest-messaging/accurest-messaging-integration/build.gradle new file mode 100644 index 0000000000..b29adf4789 --- /dev/null +++ b/accurest-messaging/accurest-messaging-integration/build.gradle @@ -0,0 +1,16 @@ +repositories { + mavenLocal() + jcenter() + maven { + url "http://repo.spring.io/snapshot" + } + maven { + url "http://repo.spring.io/milestone" + } +} + +dependencies { + compile project(':accurest-messaging-root:accurest-messaging-core') + compile 'org.springframework:spring-messaging:[4.0.0.RELEASE,)' + compile 'org.slf4j:slf4j-api:[1.6.0,)' +} \ No newline at end of file diff --git a/accurest-messaging/accurest-messaging-integration/src/main/java/io/codearte/accurest/messaging/integration/AccurestIntegrationConfiguration.java b/accurest-messaging/accurest-messaging-integration/src/main/java/io/codearte/accurest/messaging/integration/AccurestIntegrationConfiguration.java new file mode 100644 index 0000000000..214161b605 --- /dev/null +++ b/accurest-messaging/accurest-messaging-integration/src/main/java/io/codearte/accurest/messaging/integration/AccurestIntegrationConfiguration.java @@ -0,0 +1,22 @@ +package io.codearte.accurest.messaging.integration; + +import io.codearte.accurest.messaging.AccurestMessageBuilder; +import io.codearte.accurest.messaging.AccurestMessaging; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; + +/** + * @author Marcin Grzejszczak + */ +public class AccurestIntegrationConfiguration { + + @Bean + AccurestMessaging accurestMessaging(ApplicationContext applicationContext, AccurestMessageBuilder accurestMessageBuilder) { + return new AccurestIntegrationMessaging(applicationContext, accurestMessageBuilder); + } + + @Bean + AccurestMessageBuilder accurestMessageBuilder() { + return new AccurestIntegrationMessageBuilder(); + } +} diff --git a/accurest-messaging/accurest-messaging-integration/src/main/java/io/codearte/accurest/messaging/integration/AccurestIntegrationMessageBuilder.java b/accurest-messaging/accurest-messaging-integration/src/main/java/io/codearte/accurest/messaging/integration/AccurestIntegrationMessageBuilder.java new file mode 100644 index 0000000000..9b6b945570 --- /dev/null +++ b/accurest-messaging/accurest-messaging-integration/src/main/java/io/codearte/accurest/messaging/integration/AccurestIntegrationMessageBuilder.java @@ -0,0 +1,25 @@ +package io.codearte.accurest.messaging.integration; + +import io.codearte.accurest.messaging.AccurestMessage; +import io.codearte.accurest.messaging.AccurestMessageBuilder; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageHeaders; +import org.springframework.messaging.support.MessageBuilder; + +import java.util.Map; + +/** + * @author Marcin Grzejszczak + */ +public class AccurestIntegrationMessageBuilder implements AccurestMessageBuilder> { + + @Override + public AccurestMessage> create(T payload, Map headers) { + return new IntegrationMessage<>(MessageBuilder.createMessage(payload, new MessageHeaders(headers))); + } + + @Override + public AccurestMessage> create(Message message) { + return new IntegrationMessage<>(message); + } +} diff --git a/accurest-messaging/accurest-messaging-integration/src/main/java/io/codearte/accurest/messaging/integration/AccurestIntegrationMessaging.java b/accurest-messaging/accurest-messaging-integration/src/main/java/io/codearte/accurest/messaging/integration/AccurestIntegrationMessaging.java new file mode 100644 index 0000000000..2486080560 --- /dev/null +++ b/accurest-messaging/accurest-messaging-integration/src/main/java/io/codearte/accurest/messaging/integration/AccurestIntegrationMessaging.java @@ -0,0 +1,77 @@ +package io.codearte.accurest.messaging.integration; + +import io.codearte.accurest.messaging.AccurestMessage; +import io.codearte.accurest.messaging.AccurestMessageBuilder; +import io.codearte.accurest.messaging.AccurestMessaging; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageChannel; +import org.springframework.messaging.PollableChannel; +import org.springframework.stereotype.Component; + +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * @author Marcin Grzejszczak + */ +@Component +public class AccurestIntegrationMessaging implements AccurestMessaging> { + + private static final Logger log = LoggerFactory.getLogger(AccurestIntegrationMessaging.class); + + private final ApplicationContext context; + private final AccurestMessageBuilder builder; + + @Autowired + @SuppressWarnings("unchecked") + public AccurestIntegrationMessaging(ApplicationContext context, AccurestMessageBuilder accurestMessageBuilder) { + this.context = context; + this.builder = accurestMessageBuilder; + } + + @Override + public void send(AccurestMessage> message, String destination) { + try { + MessageChannel messageChannel = context.getBean(destination, MessageChannel.class); + messageChannel.send(message.convert()); + } catch (Exception e) { + log.error("Exception occurred while trying to send a message [" + message + "] " + + "to a channel with name [" + destination + "]", e); + throw e; + } + } + + @Override + @SuppressWarnings("unchecked") + public AccurestMessage> receiveMessage(String destination, long timeout, TimeUnit timeUnit) { + try { + PollableChannel messageChannel = context.getBean(destination, PollableChannel.class); + return builder.create(messageChannel.receive(timeUnit.toMillis(timeout))); + } catch (Exception e) { + log.error("Exception occurred while trying to read a message from " + + " a channel with name [" + destination + "]", e); + throw new RuntimeException(e); + } + } + + @Override + public AccurestMessage> receiveMessage(String destination) { + return receiveMessage(destination, 5, TimeUnit.SECONDS); + } + + @Override + @SuppressWarnings("unchecked") + public AccurestMessage> create(T t, Map headers) { + return builder.create(t, headers); + } + + @Override + @SuppressWarnings("unchecked") + public AccurestMessage> create(Message message) { + return builder.create(message); + } +} diff --git a/accurest-messaging/accurest-messaging-integration/src/main/java/io/codearte/accurest/messaging/integration/IntegrationMessage.java b/accurest-messaging/accurest-messaging-integration/src/main/java/io/codearte/accurest/messaging/integration/IntegrationMessage.java new file mode 100644 index 0000000000..87b472f7dd --- /dev/null +++ b/accurest-messaging/accurest-messaging-integration/src/main/java/io/codearte/accurest/messaging/integration/IntegrationMessage.java @@ -0,0 +1,38 @@ +package io.codearte.accurest.messaging.integration; + +import io.codearte.accurest.messaging.AccurestMessage; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageHeaders; + +/** + * @author Marcin Grzejszczak + */ +public class IntegrationMessage implements AccurestMessage> { + + private final Message delegate; + + public IntegrationMessage(Message delegate) { + this.delegate = delegate; + } + + @Override + public T getPayload() { + return delegate.getPayload(); + } + + @Override + public MessageHeaders getHeaders() { + return delegate.getHeaders(); + } + + @Override + public Object getHeader(String key) { + return getHeaders().get(key); + } + + @Override + public Message convert() { + return delegate; + } + +} diff --git a/accurest-messaging/accurest-messaging-integration/src/main/resources/META-INF/spring.factories b/accurest-messaging/accurest-messaging-integration/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000000..117d34083a --- /dev/null +++ b/accurest-messaging/accurest-messaging-integration/src/main/resources/META-INF/spring.factories @@ -0,0 +1,3 @@ +# Auto Configuration +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +io.codearte.accurest.messaging.integration.AccurestIntegrationConfiguration diff --git a/accurest-messaging/accurest-messaging-stream/build.gradle b/accurest-messaging/accurest-messaging-stream/build.gradle new file mode 100644 index 0000000000..4b045bb943 --- /dev/null +++ b/accurest-messaging/accurest-messaging-stream/build.gradle @@ -0,0 +1,16 @@ +repositories { + mavenLocal() + jcenter() + maven { + url "http://repo.spring.io/snapshot" + } + maven { + url "http://repo.spring.io/milestone" + } +} + +dependencies { + compile project(':accurest-messaging-root:accurest-messaging-core') + compile 'org.springframework:spring-messaging:[4.0.0.RELEASE,)' + compile 'org.springframework.cloud:spring-cloud-stream-test-support:[1.0.0.RC2,)' +} \ No newline at end of file diff --git a/accurest-messaging/accurest-messaging-stream/src/main/java/io/codearte/accurest/messaging/stream/AccurestStreamAutoConfiguration.java b/accurest-messaging/accurest-messaging-stream/src/main/java/io/codearte/accurest/messaging/stream/AccurestStreamAutoConfiguration.java new file mode 100644 index 0000000000..243f856acb --- /dev/null +++ b/accurest-messaging/accurest-messaging-stream/src/main/java/io/codearte/accurest/messaging/stream/AccurestStreamAutoConfiguration.java @@ -0,0 +1,24 @@ +package io.codearte.accurest.messaging.stream; + +import io.codearte.accurest.messaging.AccurestMessageBuilder; +import io.codearte.accurest.messaging.AccurestMessaging; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author Marcin Grzejszczak + */ +@Configuration +public class AccurestStreamAutoConfiguration { + + @Bean + AccurestMessaging accurestMessaging(ApplicationContext applicationContext, AccurestMessageBuilder accurestMessageBuilder) { + return new AccurestStreamMessaging(applicationContext, accurestMessageBuilder); + } + + @Bean + AccurestMessageBuilder accurestMessageBuilder() { + return new AccurestStreamMessageBuilder(); + } +} diff --git a/accurest-messaging/accurest-messaging-stream/src/main/java/io/codearte/accurest/messaging/stream/AccurestStreamMessageBuilder.java b/accurest-messaging/accurest-messaging-stream/src/main/java/io/codearte/accurest/messaging/stream/AccurestStreamMessageBuilder.java new file mode 100644 index 0000000000..e6abccdd89 --- /dev/null +++ b/accurest-messaging/accurest-messaging-stream/src/main/java/io/codearte/accurest/messaging/stream/AccurestStreamMessageBuilder.java @@ -0,0 +1,25 @@ +package io.codearte.accurest.messaging.stream; + +import io.codearte.accurest.messaging.AccurestMessage; +import io.codearte.accurest.messaging.AccurestMessageBuilder; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageHeaders; +import org.springframework.messaging.support.MessageBuilder; + +import java.util.Map; + +/** + * @author Marcin Grzejszczak + */ +public class AccurestStreamMessageBuilder implements AccurestMessageBuilder> { + + @Override + public AccurestMessage> create(T payload, Map headers) { + return new StreamMessage<>(MessageBuilder.createMessage(payload, new MessageHeaders(headers))); + } + + @Override + public AccurestMessage> create(Message message) { + return new StreamMessage<>(message); + } +} diff --git a/accurest-messaging/accurest-messaging-stream/src/main/java/io/codearte/accurest/messaging/stream/AccurestStreamMessaging.java b/accurest-messaging/accurest-messaging-stream/src/main/java/io/codearte/accurest/messaging/stream/AccurestStreamMessaging.java new file mode 100644 index 0000000000..368a7d5cd0 --- /dev/null +++ b/accurest-messaging/accurest-messaging-stream/src/main/java/io/codearte/accurest/messaging/stream/AccurestStreamMessaging.java @@ -0,0 +1,77 @@ +package io.codearte.accurest.messaging.stream; + +import io.codearte.accurest.messaging.AccurestMessage; +import io.codearte.accurest.messaging.AccurestMessageBuilder; +import io.codearte.accurest.messaging.AccurestMessaging; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.stream.test.binder.MessageCollector; +import org.springframework.context.ApplicationContext; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageChannel; + +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * @author Marcin Grzejszczak + */ +public class AccurestStreamMessaging implements AccurestMessaging> { + + private static final Logger log = LoggerFactory.getLogger(AccurestStreamMessaging.class); + + private final ApplicationContext context; + private final MessageCollector messageCollector; + private final AccurestMessageBuilder builder; + + @Autowired + @SuppressWarnings("unchecked") + public AccurestStreamMessaging(ApplicationContext context, AccurestMessageBuilder builder) { + this.context = context; + this.messageCollector = context.getBean(MessageCollector.class); + this.builder = builder; + } + + @Override + public void send(AccurestMessage> message, String destination) { + try { + MessageChannel messageChannel = context.getBean(destination, MessageChannel.class); + messageChannel.send(message.convert()); + } catch (Exception e) { + log.error("Exception occurred while trying to send a message [" + message + "] " + + "to a channel with name [" + destination + "]", e); + throw e; + } + } + + @Override + @SuppressWarnings("unchecked") + public AccurestMessage> receiveMessage(String destination, long timeout, TimeUnit timeUnit) { + try { + MessageChannel messageChannel = context.getBean(destination, MessageChannel.class); + return builder.create(messageCollector.forChannel(messageChannel).poll(timeout, timeUnit)); + } catch (Exception e) { + log.error("Exception occurred while trying to read a message from " + + " a channel with name [" + destination + "]", e); + throw new RuntimeException(e); + } + } + + @Override + public AccurestMessage> receiveMessage(String destination) { + return receiveMessage(destination, 5, TimeUnit.SECONDS); + } + + @Override + @SuppressWarnings("unchecked") + public AccurestMessage> create(T t, Map headers) { + return builder.create(t, headers); + } + + @Override + @SuppressWarnings("unchecked") + public AccurestMessage> create(Message message) { + return builder.create(message); + } +} diff --git a/accurest-messaging/accurest-messaging-stream/src/main/java/io/codearte/accurest/messaging/stream/StreamMessage.java b/accurest-messaging/accurest-messaging-stream/src/main/java/io/codearte/accurest/messaging/stream/StreamMessage.java new file mode 100644 index 0000000000..7b7faeb68a --- /dev/null +++ b/accurest-messaging/accurest-messaging-stream/src/main/java/io/codearte/accurest/messaging/stream/StreamMessage.java @@ -0,0 +1,38 @@ +package io.codearte.accurest.messaging.stream; + +import io.codearte.accurest.messaging.AccurestMessage; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageHeaders; + +/** + * @author Marcin Grzejszczak + */ +public class StreamMessage implements AccurestMessage> { + + private final Message delegate; + + public StreamMessage(Message delegate) { + this.delegate = delegate; + } + + @Override + public T getPayload() { + return delegate.getPayload(); + } + + @Override + public MessageHeaders getHeaders() { + return delegate.getHeaders(); + } + + @Override + public Object getHeader(String key) { + return getHeaders().get(key); + } + + @Override + public Message convert() { + return delegate; + } + +} diff --git a/accurest-messaging/accurest-messaging-stream/src/main/resources/META-INF/spring.factories b/accurest-messaging/accurest-messaging-stream/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000000..b2ba657e44 --- /dev/null +++ b/accurest-messaging/accurest-messaging-stream/src/main/resources/META-INF/spring.factories @@ -0,0 +1,3 @@ +# Auto Configuration +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +io.codearte.accurest.messaging.stream.AccurestStreamAutoConfiguration diff --git a/build.gradle b/build.gradle index b679ef0101..a58ce57c62 100644 --- a/build.gradle +++ b/build.gradle @@ -9,7 +9,6 @@ buildscript { classpath "com.bmuschko:gradle-nexus-plugin:2.3" classpath "io.codearte.gradle.nexus:gradle-nexus-staging-plugin:0.5.3" if (project.hasProperty('fatJar')) classpath 'com.github.jengelman.gradle.plugins:shadow:1.2.3' - classpath "io.spring.gradle:dependency-management-plugin:0.5.6.RELEASE" } } @@ -98,7 +97,7 @@ project(':accurest-core') { compile "com.github.tomakehurst:wiremock:$wiremockVersion" compile "com.toomuchcoding.jsonassert:jsonassert:$jsonassertVersion" compile 'org.assertj:assertj-core:2.3.0' - compile 'org.codehaus.groovy:groovy-all:2.4.4' + compile localGroovy() testCompile 'cglib:cglib-nodep:2.2' testCompile 'org.objenesis:objenesis:2.1' testCompile project(':accurest-testing-utils') @@ -129,8 +128,8 @@ project(':accurest-gradle-plugin') { dependencies { compile project(':accurest-core') compile project(':accurest-converters') - compile gradleApi() + testCompile('com.netflix.nebula:nebula-test:4.0.0') { exclude(group: 'org.spockframework') } @@ -148,6 +147,10 @@ project(':accurest-gradle-plugin') { } } + // Hack for reusing this in the plugin test + test.dependsOn(':accurest-core:install') + test.dependsOn(':accurest-messaging-root:accurest-messaging-core:install') + test.dependsOn(':accurest-messaging-root:accurest-messaging-integration:install') uploadArchives.dependsOn { funcTest } } diff --git a/gradle.properties b/gradle.properties index dfc935df68..00b1483625 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,4 +2,6 @@ nexusUsername = nexusPassword = wiremockVersion = 2.0.5-beta -jsonassertVersion = 0.2.2 \ No newline at end of file +jsonassertVersion = 0.2.2 + +BOM_VERSION=Brixton-1.0.0.RC1 \ No newline at end of file diff --git a/samples/messaging-camel/build.gradle b/samples/messaging-camel/build.gradle new file mode 100644 index 0000000000..bc1c2750b3 --- /dev/null +++ b/samples/messaging-camel/build.gradle @@ -0,0 +1,26 @@ +repositories { + mavenLocal() + jcenter() + maven { + url "http://repo.spring.io/snapshot" + } + maven { + url "http://repo.spring.io/milestone" + } +} + +dependencies { + compile project(':accurest-core') + compile 'org.springframework.boot:spring-boot-starter-actuator:1.3.3.RELEASE' + compile 'org.apache.camel:camel-spring-boot-starter:2.17.0' + compile 'org.apache.camel:camel-jms:2.17.0' + compile 'org.apache.activemq:activemq-camel:5.12.1' + compile 'org.apache.activemq:activemq-pool:5.12.1' + compile 'org.apache.camel:camel-jackson:2.17.0' + + testCompile project(':accurest-messaging-root:accurest-messaging-camel') + testCompile 'org.springframework.boot:spring-boot-starter-test:1.3.3.RELEASE' + testCompile('org.spockframework:spock-spring:1.0-groovy-2.4') { + exclude(group: 'org.codehaus.groovy') + } +} \ No newline at end of file diff --git a/samples/messaging-camel/src/main/groovy/io/codearte/accurest/samples/camel/BookDeleted.groovy b/samples/messaging-camel/src/main/groovy/io/codearte/accurest/samples/camel/BookDeleted.groovy new file mode 100644 index 0000000000..f20d028575 --- /dev/null +++ b/samples/messaging-camel/src/main/groovy/io/codearte/accurest/samples/camel/BookDeleted.groovy @@ -0,0 +1,14 @@ +package io.codearte.accurest.samples.camel + +import com.fasterxml.jackson.annotation.JsonCreator +import groovy.transform.CompileStatic + +@CompileStatic +class BookDeleted implements Serializable { + final String bookName + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + BookDeleted(String bookName) { + this.bookName = bookName + } +} diff --git a/samples/messaging-camel/src/main/groovy/io/codearte/accurest/samples/camel/BookDeleter.groovy b/samples/messaging-camel/src/main/groovy/io/codearte/accurest/samples/camel/BookDeleter.groovy new file mode 100644 index 0000000000..9184450edc --- /dev/null +++ b/samples/messaging-camel/src/main/groovy/io/codearte/accurest/samples/camel/BookDeleter.groovy @@ -0,0 +1,28 @@ +package io.codearte.accurest.samples.camel + +import groovy.transform.CompileStatic +import groovy.util.logging.Slf4j +import org.apache.camel.Exchange +import org.springframework.stereotype.Component + +import java.util.concurrent.atomic.AtomicBoolean + +@CompileStatic +@Slf4j +@Component +class BookDeleter { + + /** + Scenario for "should generate tests triggered by a message": + client side: if sends a message to input.messageFrom then message will be sent to output.messageFrom + server side: will send a message to input, verify the message contents and await upon receiving message on the output messageFrom + */ + void bookDeleted(Exchange exchange) { + BookDeleted bookDeleted = exchange.in.getBody(BookDeleted) + log.info("Deleting book [$bookDeleted]") + bookSuccessfulyDeleted.set(true) + log.info("Book successfuly deleted [$bookSuccessfulyDeleted]") + } + + AtomicBoolean bookSuccessfulyDeleted = new AtomicBoolean(false) +} diff --git a/samples/messaging-camel/src/main/groovy/io/codearte/accurest/samples/camel/BookReturned.groovy b/samples/messaging-camel/src/main/groovy/io/codearte/accurest/samples/camel/BookReturned.groovy new file mode 100644 index 0000000000..19ca5837d9 --- /dev/null +++ b/samples/messaging-camel/src/main/groovy/io/codearte/accurest/samples/camel/BookReturned.groovy @@ -0,0 +1,14 @@ +package io.codearte.accurest.samples.camel + +import com.fasterxml.jackson.annotation.JsonCreator +import groovy.transform.CompileStatic + +@CompileStatic +class BookReturned implements Serializable { + final String bookName + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + BookReturned(String bookName) { + this.bookName = bookName + } +} diff --git a/samples/messaging-camel/src/main/groovy/io/codearte/accurest/samples/camel/BookRouteConfiguration.groovy b/samples/messaging-camel/src/main/groovy/io/codearte/accurest/samples/camel/BookRouteConfiguration.groovy new file mode 100644 index 0000000000..66f5a46535 --- /dev/null +++ b/samples/messaging-camel/src/main/groovy/io/codearte/accurest/samples/camel/BookRouteConfiguration.groovy @@ -0,0 +1,37 @@ +package io.codearte.accurest.samples.camel + +import org.apache.activemq.camel.component.ActiveMQComponent +import org.apache.camel.RoutesBuilder +import org.apache.camel.model.dataformat.JsonLibrary +import org.apache.camel.spring.SpringRouteBuilder +import org.springframework.beans.factory.annotation.Value +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +/** + * @author Marcin Grzejszczak + */ +@Configuration +class BookRouteConfiguration { + + @Bean + ActiveMQComponent activeMQComponent(@Value('${activemq.url:vm://localhost?broker.persistent=false}') String url) { + return new ActiveMQComponent(brokerURL: url) + } + + @Bean + RoutesBuilder myRouter(BookService bookService, BookDeleter bookDeleter) { + return new SpringRouteBuilder() { + + @Override + public void configure() throws Exception { + // scenario 1 - from bean to output + from("direct:start").unmarshal().json(JsonLibrary.Jackson, BookReturned).bean(bookService).to("jms:output") + // scenario 2 - from input to output + from("jms:input").unmarshal().json(JsonLibrary.Jackson, BookReturned).bean(bookService).to("jms:output") + // scenario 3 - from input to no output + from("jms:delete").unmarshal().json(JsonLibrary.Jackson, BookDeleted).bean(bookDeleter) + } + + }; + } +} diff --git a/samples/messaging-camel/src/main/groovy/io/codearte/accurest/samples/camel/BookService.groovy b/samples/messaging-camel/src/main/groovy/io/codearte/accurest/samples/camel/BookService.groovy new file mode 100644 index 0000000000..edda66c9c7 --- /dev/null +++ b/samples/messaging-camel/src/main/groovy/io/codearte/accurest/samples/camel/BookService.groovy @@ -0,0 +1,28 @@ +package io.codearte.accurest.samples.camel + +import groovy.transform.CompileStatic +import groovy.util.logging.Slf4j +import org.apache.camel.Exchange +import org.springframework.stereotype.Service + +@Service +@CompileStatic +@Slf4j +class BookService { + + /** + Scenario for "should generate tests triggered by a method": + client side: must have a possibility to "trigger" sending of a message to the given messageFrom + server side: will run the method and await upon receiving message on the output messageFrom + + Method triggers sending a message to a source + */ + void returnBook(Exchange exchange) { + BookReturned bookReturned = exchange.in.getBody(BookReturned) + log.info("Returning book [$bookReturned]") + exchange.out.with { + body = bookReturned + setHeader('BOOK-NAME', bookReturned.bookName) + } + } +} diff --git a/samples/messaging-camel/src/main/groovy/io/codearte/accurest/samples/camel/CamelMessagingApplication.groovy b/samples/messaging-camel/src/main/groovy/io/codearte/accurest/samples/camel/CamelMessagingApplication.groovy new file mode 100644 index 0000000000..26819753e5 --- /dev/null +++ b/samples/messaging-camel/src/main/groovy/io/codearte/accurest/samples/camel/CamelMessagingApplication.groovy @@ -0,0 +1,12 @@ +package io.codearte.accurest.samples.camel + +import org.springframework.boot.SpringApplication +import org.springframework.boot.autoconfigure.SpringBootApplication + +@SpringBootApplication +class CamelMessagingApplication { + + static void main(String[] args) { + SpringApplication.run(CamelMessagingApplication.class, args) + } +} diff --git a/samples/messaging-camel/src/test/groovy/io/codearte/accurest/samples/camel/CamelMessagingApplicationSpec.groovy b/samples/messaging-camel/src/test/groovy/io/codearte/accurest/samples/camel/CamelMessagingApplicationSpec.groovy new file mode 100644 index 0000000000..0400c9d359 --- /dev/null +++ b/samples/messaging-camel/src/test/groovy/io/codearte/accurest/samples/camel/CamelMessagingApplicationSpec.groovy @@ -0,0 +1,144 @@ +package io.codearte.accurest.samples.camel + +import com.fasterxml.jackson.databind.ObjectMapper +import com.jayway.jsonpath.DocumentContext +import com.jayway.jsonpath.JsonPath +import com.toomuchcoding.jsonassert.JsonAssertion +import io.codearte.accurest.dsl.GroovyDsl +import io.codearte.accurest.messaging.AccurestMessage +import io.codearte.accurest.messaging.AccurestMessaging +import org.apache.camel.model.ModelCamelContext +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.SpringApplicationContextLoader +import org.springframework.test.context.ContextConfiguration +import spock.lang.Specification +import spock.util.concurrent.PollingConditions + +import javax.inject.Inject +/** + * SPIKE ON TESTS FROM NOTES IN MessagingSpec + */ +// Context configuration would end up in base class +@ContextConfiguration(classes = [CamelMessagingApplication], loader = SpringApplicationContextLoader) +public class CamelMessagingApplicationSpec extends Specification { + + // ALL CASES + @Inject AccurestMessaging accurestMessaging + ObjectMapper accurestObjectMapper = new ObjectMapper() + + def "should work for triggered based messaging"() { + given: + def dsl = GroovyDsl.make { + label 'some_label' + input { + triggeredBy('bookReturnedTriggered()') + } + outputMessage { + sentTo('activemq:output') + body('''{ "bookName" : "foo" }''') + headers { + header('BOOK-NAME', 'foo') + } + } + } + // generated test should look like this: + when: + bookReturnedTriggered() + then: + def response = accurestMessaging.receiveMessage('activemq:output') + response.headers.get('BOOK-NAME') == 'foo' + and: + DocumentContext parsedJson = JsonPath.parse(accurestObjectMapper.writeValueAsString(response.payload)) + JsonAssertion.assertThat(parsedJson).field('bookName').isEqualTo('foo') + } + + def "should generate tests triggered by a message"() { + given: + def dsl = GroovyDsl.make { + label 'some_label' + input { + messageFrom('jms:input') + messageBody([ + bookName: 'foo' + ]) + messageHeaders { + header('sample', 'header') + header('Content-Type', 'application/json') + } + } + outputMessage { + sentTo('jms:output') + body([ + bookName: 'foo' + ]) + headers { + header('BOOK-NAME', 'foo') + } + } + } + + // generated test should look like this: + + //given: + AccurestMessage inputMessage = accurestMessaging.create( + accurestObjectMapper.writeValueAsString([bookName: 'foo']), + [sample: 'header'] + ) + when: + accurestMessaging.send(inputMessage, 'jms:input') + then: + def response = accurestMessaging.receiveMessage('jms:output') + response.headers.get('BOOK-NAME') == 'foo' + and: + DocumentContext parsedJson = JsonPath.parse(accurestObjectMapper.writeValueAsString(response.payload)) + JsonAssertion.assertThat(parsedJson).field('bookName').isEqualTo('foo') + } + + def "should generate tests without destination, triggered by a message"() { + given: + def dsl = GroovyDsl.make { + label 'some_label' + input { + messageFrom('jms:delete') + messageBody([ + bookName: 'foo' + ]) + messageHeaders { + header('sample', 'header') + } + assertThat('bookWasDeleted()') + } + } + + // generated test should look like this: + + //given: + AccurestMessage inputMessage = accurestMessaging.create( + accurestObjectMapper.writeValueAsString([bookName: 'foo']), + [sample: 'header'] + ) + when: + accurestMessaging.send(inputMessage, 'jms:delete') + then: + noExceptionThrown() + bookWasDeleted() + } + + // BASE CLASS WOULD HAVE THIS: + + @Autowired ModelCamelContext camelContext + @Autowired BookDeleter bookDeleter + + void bookReturnedTriggered() { + camelContext.createProducerTemplate().sendBody('direct:start', '''{"bookName" : "foo" }''') + } + + PollingConditions pollingConditions = new PollingConditions() + + void bookWasDeleted() { + pollingConditions.eventually { + assert bookDeleter.bookSuccessfulyDeleted.get() + } + } + +} \ No newline at end of file diff --git a/samples/messaging-integration/build.gradle b/samples/messaging-integration/build.gradle new file mode 100644 index 0000000000..5096207045 --- /dev/null +++ b/samples/messaging-integration/build.gradle @@ -0,0 +1,23 @@ +repositories { + mavenLocal() + jcenter() + maven { + url "http://repo.spring.io/snapshot" + } + maven { + url "http://repo.spring.io/milestone" + } +} + +dependencies { + compile project(':accurest-core') + compile 'org.springframework.boot:spring-boot-starter-actuator:1.3.3.RELEASE' + compile 'org.springframework.boot:spring-boot-starter-integration:1.3.3.RELEASE' + compile 'org.springframework:spring-messaging:[4.0.0.RELEASE,)' + + testCompile project(':accurest-messaging-root:accurest-messaging-integration') + testCompile 'org.springframework.boot:spring-boot-starter-test:1.3.3.RELEASE' + testCompile('org.spockframework:spock-spring:1.0-groovy-2.4') { + exclude(group: 'org.codehaus.groovy') + } +} \ No newline at end of file diff --git a/samples/messaging-integration/src/main/groovy/io/codearte/accurest/samples/messaging/BookDeleted.groovy b/samples/messaging-integration/src/main/groovy/io/codearte/accurest/samples/messaging/BookDeleted.groovy new file mode 100644 index 0000000000..abe8ce0959 --- /dev/null +++ b/samples/messaging-integration/src/main/groovy/io/codearte/accurest/samples/messaging/BookDeleted.groovy @@ -0,0 +1,14 @@ +package io.codearte.accurest.samples.messaging + +import com.fasterxml.jackson.annotation.JsonCreator +import groovy.transform.CompileStatic + +@CompileStatic +class BookDeleted { + final String bookName + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + BookDeleted(String bookName) { + this.bookName = bookName + } +} diff --git a/samples/messaging-integration/src/main/groovy/io/codearte/accurest/samples/messaging/BookListener.groovy b/samples/messaging-integration/src/main/groovy/io/codearte/accurest/samples/messaging/BookListener.groovy new file mode 100644 index 0000000000..1ef7d26f79 --- /dev/null +++ b/samples/messaging-integration/src/main/groovy/io/codearte/accurest/samples/messaging/BookListener.groovy @@ -0,0 +1,38 @@ +package io.codearte.accurest.samples.messaging + +import groovy.transform.CompileStatic +import groovy.util.logging.Slf4j +import org.springframework.messaging.Message +import org.springframework.messaging.MessageHeaders +import org.springframework.messaging.support.MessageBuilder + +import java.util.concurrent.atomic.AtomicBoolean + +@CompileStatic +@Slf4j +class BookListener { + + /** + Scenario for "should generate tests triggered by a message": + client side: if sends a message to input.messageFrom then message will be sent to output.messageFrom + server side: will send a message to input, verify the message contents and await upon receiving message on the output messageFrom + */ + Message returnBook(BookReturned bookReturned) { + log.info("Returning book [$bookReturned]") + return MessageBuilder.createMessage(bookReturned, new MessageHeaders([ + 'BOOK-NAME': bookReturned.bookName as Object + ])) + } + + /** + Scenario for "should generate tests triggered by a message": + client side: if sends a message to input.messageFrom then message will be sent to output.messageFrom + server side: will send a message to input, verify the message contents and await upon receiving message on the output messageFrom + */ + void bookDeleted(BookDeleted bookDeleted) { + log.info("Deleting book [$bookDeleted]") + bookSuccessfulyDeleted.set(true) + } + + AtomicBoolean bookSuccessfulyDeleted = new AtomicBoolean(false) +} diff --git a/samples/messaging-integration/src/main/groovy/io/codearte/accurest/samples/messaging/BookReturned.groovy b/samples/messaging-integration/src/main/groovy/io/codearte/accurest/samples/messaging/BookReturned.groovy new file mode 100644 index 0000000000..cca077e266 --- /dev/null +++ b/samples/messaging-integration/src/main/groovy/io/codearte/accurest/samples/messaging/BookReturned.groovy @@ -0,0 +1,14 @@ +package io.codearte.accurest.samples.messaging + +import com.fasterxml.jackson.annotation.JsonCreator +import groovy.transform.CompileStatic + +@CompileStatic +class BookReturned { + final String bookName + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + BookReturned(String bookName) { + this.bookName = bookName + } +} diff --git a/samples/messaging-integration/src/main/groovy/io/codearte/accurest/samples/messaging/BookService.groovy b/samples/messaging-integration/src/main/groovy/io/codearte/accurest/samples/messaging/BookService.groovy new file mode 100644 index 0000000000..f5acc1c1e8 --- /dev/null +++ b/samples/messaging-integration/src/main/groovy/io/codearte/accurest/samples/messaging/BookService.groovy @@ -0,0 +1,32 @@ +package io.codearte.accurest.samples.messaging + +import groovy.transform.CompileStatic +import groovy.util.logging.Slf4j +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.messaging.MessageChannel +import org.springframework.messaging.MessageHeaders +import org.springframework.messaging.support.MessageBuilder +import org.springframework.stereotype.Service + +@Service +@CompileStatic +@Slf4j +class BookService { + + @Autowired @Qualifier("outputChannel") MessageChannel outputChannel + + /** + Scenario for "should generate tests triggered by a method": + client side: must have a possibility to "trigger" sending of a message to the given messageFrom + server side: will run the method and await upon receiving message on the output messageFrom + + Method triggers sending a message to a source + */ + void returnBook(BookReturned bookReturned) { + log.info("Returning book [$bookReturned]") + outputChannel.send(MessageBuilder.createMessage(bookReturned, new MessageHeaders([ + 'BOOK-NAME': bookReturned.bookName as Object + ]))) + } +} diff --git a/samples/messaging-integration/src/main/groovy/io/codearte/accurest/samples/messaging/IntegrationMessagingApplication.groovy b/samples/messaging-integration/src/main/groovy/io/codearte/accurest/samples/messaging/IntegrationMessagingApplication.groovy new file mode 100644 index 0000000000..e1ec8da114 --- /dev/null +++ b/samples/messaging-integration/src/main/groovy/io/codearte/accurest/samples/messaging/IntegrationMessagingApplication.groovy @@ -0,0 +1,14 @@ +package io.codearte.accurest.samples.messaging + +import org.springframework.boot.SpringApplication +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.context.annotation.ImportResource + +@SpringBootApplication +@ImportResource("classpath*:integration-context.xml") +class IntegrationMessagingApplication { + + static void main(String[] args) { + SpringApplication.run(IntegrationMessagingApplication.class, args) + } +} diff --git a/samples/messaging-integration/src/main/resources/integration-context.xml b/samples/messaging-integration/src/main/resources/integration-context.xml new file mode 100644 index 0000000000..aa5a5cc3a5 --- /dev/null +++ b/samples/messaging-integration/src/main/resources/integration-context.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/messaging-integration/src/test/groovy/io/codearte/accurest/samples/messaging/IntegrationMessagingApplicationSpec.groovy b/samples/messaging-integration/src/test/groovy/io/codearte/accurest/samples/messaging/IntegrationMessagingApplicationSpec.groovy new file mode 100644 index 0000000000..6b8cfeda18 --- /dev/null +++ b/samples/messaging-integration/src/test/groovy/io/codearte/accurest/samples/messaging/IntegrationMessagingApplicationSpec.groovy @@ -0,0 +1,138 @@ +package io.codearte.accurest.samples.messaging + +import com.fasterxml.jackson.databind.ObjectMapper +import com.jayway.jsonpath.DocumentContext +import com.jayway.jsonpath.JsonPath +import com.toomuchcoding.jsonassert.JsonAssertion +import io.codearte.accurest.dsl.GroovyDsl +import io.codearte.accurest.messaging.AccurestMessage +import io.codearte.accurest.messaging.AccurestMessaging +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.SpringApplicationContextLoader +import org.springframework.test.context.ContextConfiguration +import spock.lang.Specification + +import javax.inject.Inject + +/** + * SPIKE ON TESTS FROM NOTES IN MessagingSpec + */ +// Context configuration would end up in base class +@ContextConfiguration(classes = [IntegrationMessagingApplication], loader = SpringApplicationContextLoader) +public class IntegrationMessagingApplicationSpec extends Specification { + + // ALL CASES + @Inject AccurestMessaging accurestMessaging + ObjectMapper accurestObjectMapper = new ObjectMapper() + + def "should work for triggered based messaging"() { + given: + def dsl = GroovyDsl.make { + label 'some_label' + input { + triggeredBy('bookReturnedTriggered()') + } + outputMessage { + sentTo('output') + body('''{ "bookName" : "foo" }''') + headers { + header('BOOK-NAME', 'foo') + } + } + } + // generated test should look like this: + when: + bookReturnedTriggered() + then: + def response = accurestMessaging.receiveMessage('output') + response.headers.get('BOOK-NAME') == 'foo' + and: + DocumentContext parsedJson = JsonPath.parse(accurestObjectMapper.writeValueAsString(response.payload)) + JsonAssertion.assertThat(parsedJson).field('bookName').isEqualTo('foo') + } + + def "should generate tests triggered by a message"() { + given: + def dsl = GroovyDsl.make { + label 'some_label' + input { + messageFrom('input') + messageBody([ + bookName: 'foo' + ]) + messageHeaders { + header('sample', 'header') + } + } + outputMessage { + sentTo('output') + body([ + bookName: 'foo' + ]) + headers { + header('BOOK-NAME', 'foo') + } + } + } + + // generated test should look like this: + + //given: + AccurestMessage inputMessage = accurestMessaging.create( + accurestObjectMapper.writeValueAsString([bookName: 'foo']), + [sample: 'header'] + ) + when: + accurestMessaging.send(inputMessage, 'input') + then: + def response = accurestMessaging.receiveMessage('output') + response.headers.get('BOOK-NAME') == 'foo' + and: + DocumentContext parsedJson = JsonPath.parse(accurestObjectMapper.writeValueAsString(response.payload)) + JsonAssertion.assertThat(parsedJson).field('bookName').isEqualTo('foo') + } + + def "should generate tests without destination, triggered by a message"() { + given: + def dsl = GroovyDsl.make { + label 'some_label' + input { + messageFrom('delete') + messageBody([ + bookName: 'foo' + ]) + messageHeaders { + header('sample', 'header') + } + assertThat('bookWasDeleted()') + } + } + + // generated test should look like this: + + //given: + AccurestMessage inputMessage = accurestMessaging.create( + accurestObjectMapper.writeValueAsString([bookName: 'foo']), + [sample: 'header'] + ) + when: + accurestMessaging.send(inputMessage, 'delete') + then: + noExceptionThrown() + bookWasDeleted() + } + + // BASE CLASS WOULD HAVE THIS: + + @Autowired BookService bookService + @Autowired BookListener bookListener + + void bookReturnedTriggered() { + bookService.returnBook(new BookReturned("foo")) + } + + void bookWasDeleted() { + assert bookListener.bookSuccessfulyDeleted.get() + } + +} \ No newline at end of file diff --git a/samples/messaging-spring/build.gradle b/samples/messaging-spring/build.gradle new file mode 100644 index 0000000000..a422f0a484 --- /dev/null +++ b/samples/messaging-spring/build.gradle @@ -0,0 +1,25 @@ +repositories { + mavenLocal() + jcenter() + maven { + url "http://repo.spring.io/snapshot" + } + maven { + url "http://repo.spring.io/milestone" + } +} + +dependencies { + compile project(':accurest-core') + compile 'org.springframework.boot:spring-boot-starter-actuator:1.3.3.RELEASE' + compile 'org.springframework:spring-jms:4.2.3.RELEASE' + + compile 'org.apache.activemq:activemq-broker:5.12.1' + compile 'org.apache.activemq:activemq-pool:5.12.1' + + testCompile project(':accurest-messaging-root:accurest-messaging-core') + testCompile 'org.springframework.boot:spring-boot-starter-test:1.3.3.RELEASE' + testCompile('org.spockframework:spock-spring:1.0-groovy-2.4') { + exclude(group: 'org.codehaus.groovy') + } +} \ No newline at end of file diff --git a/samples/messaging-spring/src/main/groovy/io/codearte/accurest/samples/spring/BookDeleted.groovy b/samples/messaging-spring/src/main/groovy/io/codearte/accurest/samples/spring/BookDeleted.groovy new file mode 100644 index 0000000000..470329b7a8 --- /dev/null +++ b/samples/messaging-spring/src/main/groovy/io/codearte/accurest/samples/spring/BookDeleted.groovy @@ -0,0 +1,14 @@ +package io.codearte.accurest.samples.spring + +import com.fasterxml.jackson.annotation.JsonCreator +import groovy.transform.CompileStatic + +@CompileStatic +class BookDeleted implements Serializable { + final String bookName + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + BookDeleted(String bookName) { + this.bookName = bookName + } +} diff --git a/samples/messaging-spring/src/main/groovy/io/codearte/accurest/samples/spring/BookListener.groovy b/samples/messaging-spring/src/main/groovy/io/codearte/accurest/samples/spring/BookListener.groovy new file mode 100644 index 0000000000..6d56d0f594 --- /dev/null +++ b/samples/messaging-spring/src/main/groovy/io/codearte/accurest/samples/spring/BookListener.groovy @@ -0,0 +1,58 @@ +package io.codearte.accurest.samples.spring + +import com.fasterxml.jackson.databind.ObjectMapper +import groovy.transform.CompileStatic +import groovy.util.logging.Slf4j +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.jms.annotation.JmsListener +import org.springframework.jms.core.JmsTemplate +import org.springframework.jms.core.MessageCreator +import org.springframework.stereotype.Service + +import javax.jms.JMSException +import javax.jms.Message +import javax.jms.Session +import java.util.concurrent.atomic.AtomicBoolean + +@CompileStatic +@Slf4j +@Service +class BookListener { + + @Autowired JmsTemplate jmsTemplate + ObjectMapper objectMapper = new ObjectMapper() + + /** + Scenario for "should generate tests triggered by a message": + client side: if sends a message to input.messageFrom then message will be sent to output.messageFrom + server side: will send a message to input, verify the message contents and await upon receiving message on the output messageFrom + */ + @JmsListener(destination = "input") + void returnBook(String messageAsString) { + BookReturned bookReturned = objectMapper.readerFor(BookReturned).readValue(messageAsString) as BookReturned + log.info("Returning book [$bookReturned]") + MessageCreator messageCreator = new MessageCreator() { + @Override + public Message createMessage(Session session) throws JMSException { + Message message = session.createObjectMessage(bookReturned); + message.setStringProperty('BOOK-NAME', bookReturned.bookName) + return message + } + }; + jmsTemplate.send('output', messageCreator) + } + + /** + Scenario for "should generate tests triggered by a message": + client side: if sends a message to input.messageFrom then message will be sent to output.messageFrom + server side: will send a message to input, verify the message contents and await upon receiving message on the output messageFrom + */ + @JmsListener(destination = "delete") + void bookDeleted(String bookDeletedAsString) { + BookDeleted bookDeleted = objectMapper.readerFor(BookDeleted).readValue(bookDeletedAsString) as BookDeleted + log.info("Deleting book [$bookDeleted]") + bookSuccessfulyDeleted.set(true) + } + + AtomicBoolean bookSuccessfulyDeleted = new AtomicBoolean(false) +} diff --git a/samples/messaging-spring/src/main/groovy/io/codearte/accurest/samples/spring/BookReturned.groovy b/samples/messaging-spring/src/main/groovy/io/codearte/accurest/samples/spring/BookReturned.groovy new file mode 100644 index 0000000000..bd13a4ff6b --- /dev/null +++ b/samples/messaging-spring/src/main/groovy/io/codearte/accurest/samples/spring/BookReturned.groovy @@ -0,0 +1,14 @@ +package io.codearte.accurest.samples.spring + +import com.fasterxml.jackson.annotation.JsonCreator +import groovy.transform.CompileStatic + +@CompileStatic +class BookReturned implements Serializable { + String bookName + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + BookReturned(String bookName) { + this.bookName = bookName + } +} diff --git a/samples/messaging-spring/src/main/groovy/io/codearte/accurest/samples/spring/BookService.groovy b/samples/messaging-spring/src/main/groovy/io/codearte/accurest/samples/spring/BookService.groovy new file mode 100644 index 0000000000..5ba455d8c1 --- /dev/null +++ b/samples/messaging-spring/src/main/groovy/io/codearte/accurest/samples/spring/BookService.groovy @@ -0,0 +1,40 @@ +package io.codearte.accurest.samples.spring + +import groovy.transform.CompileStatic +import groovy.util.logging.Slf4j +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.jms.core.JmsTemplate +import org.springframework.jms.core.MessageCreator +import org.springframework.stereotype.Service + +import javax.jms.JMSException +import javax.jms.Message +import javax.jms.Session + +@Service +@CompileStatic +@Slf4j +class BookService { + + @Autowired JmsTemplate jmsTemplate + + /** + Scenario for "should generate tests triggered by a method": + client side: must have a possibility to "trigger" sending of a message to the given messageFrom + server side: will run the method and await upon receiving message on the output messageFrom + + Method triggers sending a message to a source + */ + void returnBook(BookReturned bookReturned ) { + log.info("Returning book [$bookReturned]") + MessageCreator messageCreator = new MessageCreator() { + @Override + public Message createMessage(Session session) throws JMSException { + Message message = session.createObjectMessage(bookReturned); + message.setStringProperty('BOOK-NAME', bookReturned.bookName) + return message + } + }; + jmsTemplate.send('output', messageCreator) + } +} diff --git a/samples/messaging-spring/src/main/groovy/io/codearte/accurest/samples/spring/SpringMessagingApplication.groovy b/samples/messaging-spring/src/main/groovy/io/codearte/accurest/samples/spring/SpringMessagingApplication.groovy new file mode 100644 index 0000000000..287a1e2240 --- /dev/null +++ b/samples/messaging-spring/src/main/groovy/io/codearte/accurest/samples/spring/SpringMessagingApplication.groovy @@ -0,0 +1,14 @@ +package io.codearte.accurest.samples.spring + +import org.springframework.boot.SpringApplication +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.jms.annotation.EnableJms + +@SpringBootApplication +@EnableJms +class SpringMessagingApplication { + + static void main(String[] args) { + SpringApplication.run(SpringMessagingApplication.class, args) + } +} diff --git a/samples/messaging-spring/src/test/groovy/io/codearte/accurest/samples/spring/SpringApplicationSpec.groovy b/samples/messaging-spring/src/test/groovy/io/codearte/accurest/samples/spring/SpringApplicationSpec.groovy new file mode 100644 index 0000000000..a6296a02ae --- /dev/null +++ b/samples/messaging-spring/src/test/groovy/io/codearte/accurest/samples/spring/SpringApplicationSpec.groovy @@ -0,0 +1,143 @@ +package io.codearte.accurest.samples.spring + +import com.fasterxml.jackson.databind.ObjectMapper +import com.jayway.jsonpath.DocumentContext +import com.jayway.jsonpath.JsonPath +import com.toomuchcoding.jsonassert.JsonAssertion +import io.codearte.accurest.dsl.GroovyDsl +import io.codearte.accurest.messaging.AccurestMessage +import io.codearte.accurest.messaging.AccurestMessaging +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.SpringApplicationContextLoader +import org.springframework.test.context.ContextConfiguration +import spock.lang.Specification +import spock.util.concurrent.PollingConditions + +import javax.inject.Inject + +/** + * SPIKE ON TESTS FROM NOTES IN MessagingSpec + */ +// Context configuration would end up in base class +@ContextConfiguration(classes = [SpringMessagingApplication], loader = SpringApplicationContextLoader) +public class SpringApplicationSpec extends Specification { + + // ALL CASES + @Inject AccurestMessaging accurestMessaging + ObjectMapper accurestObjectMapper = new ObjectMapper() + + def "should work for triggered based messaging"() { + given: + def dsl = GroovyDsl.make { + label 'some_label' + input { + triggeredBy('bookReturnedTriggered()') + } + outputMessage { + sentTo('output') + body('''{ "bookName" : "foo" }''') + headers { + header('BOOK-NAME', 'foo') + } + } + } + // generated test should look like this: + when: + bookReturnedTriggered() + then: + def response = accurestMessaging.receiveMessage('output') + response.headers.get('BOOK-NAME') == 'foo' + and: + DocumentContext parsedJson = JsonPath.parse(accurestObjectMapper.writeValueAsString(response.payload)) + JsonAssertion.assertThat(parsedJson).field('bookName').isEqualTo('foo') + } + + def "should generate tests triggered by a message"() { + given: + def dsl = GroovyDsl.make { + label 'some_label' + input { + messageFrom('input') + messageBody([ + bookName: 'foo' + ]) + messageHeaders { + header('sample', 'header') + } + } + outputMessage { + sentTo('output') + body([ + bookName: 'foo' + ]) + headers { + header('BOOK-NAME', 'foo') + } + } + } + + // generated test should look like this: + + //given: + AccurestMessage inputMessage = accurestMessaging.create( + accurestObjectMapper.writeValueAsString([bookName: 'foo']), + [sample: 'header'] + ) + when: + accurestMessaging.send(inputMessage, 'input') + then: + def response = accurestMessaging.receiveMessage('output') + response.headers.get('BOOK-NAME') == 'foo' + and: + DocumentContext parsedJson = JsonPath.parse(accurestObjectMapper.writeValueAsString(response.payload)) + JsonAssertion.assertThat(parsedJson).field('bookName').isEqualTo('foo') + } + + def "should generate tests without destination, triggered by a message"() { + given: + def dsl = GroovyDsl.make { + label 'some_label' + input { + messageFrom('delete') + messageBody([ + bookName: 'foo' + ]) + messageHeaders { + header('sample', 'header') + } + assertThat('bookWasDeleted()') + } + } + + // generated test should look like this: + + //given: + AccurestMessage inputMessage = accurestMessaging.create( + accurestObjectMapper.writeValueAsString([bookName: 'foo']), + [sample: 'header'] + ) + when: + accurestMessaging.send(inputMessage, 'delete') + then: + noExceptionThrown() + bookWasDeleted() + } + + // BASE CLASS WOULD HAVE THIS: + + @Autowired BookService bookService + @Autowired BookListener bookListener + + void bookReturnedTriggered() { + bookService.returnBook(new BookReturned("foo")) + } + + PollingConditions pollingConditions = new PollingConditions() + + void bookWasDeleted() { + pollingConditions.eventually { + assert bookListener.bookSuccessfulyDeleted.get() + } + } + +} \ No newline at end of file diff --git a/samples/messaging-spring/src/test/groovy/io/codearte/accurest/samples/spring/config/AccurestSpringMessageBuilder.groovy b/samples/messaging-spring/src/test/groovy/io/codearte/accurest/samples/spring/config/AccurestSpringMessageBuilder.groovy new file mode 100644 index 0000000000..cf3294b4e1 --- /dev/null +++ b/samples/messaging-spring/src/test/groovy/io/codearte/accurest/samples/spring/config/AccurestSpringMessageBuilder.groovy @@ -0,0 +1,35 @@ +package io.codearte.accurest.samples.spring.config + +import io.codearte.accurest.messaging.AccurestMessage +import io.codearte.accurest.messaging.AccurestMessageBuilder +import org.springframework.jms.core.MessageCreator + +import javax.jms.JMSException +import javax.jms.Message +import javax.jms.Session +/** + * @author Marcin Grzejszczak + */ +public class AccurestSpringMessageBuilder implements AccurestMessageBuilder { + + @Override + public AccurestMessage create(final T payload, final Map headers) { + MessageCreator messageCreator = new MessageCreator() { + @Override + public Message createMessage(Session session) throws JMSException { + Message message = session.createObjectMessage(payload); + headers.entrySet().each { Map.Entry entry -> + message.setObjectProperty(entry.key, entry.value) + } + return message + } + }; + + return new SpringMessage<>(messageCreator) + } + + @Override + public AccurestMessage create(Message message) { + return new SpringMessage<>(message); + } +} diff --git a/samples/messaging-spring/src/test/groovy/io/codearte/accurest/samples/spring/config/AccurestSpringMessaging.groovy b/samples/messaging-spring/src/test/groovy/io/codearte/accurest/samples/spring/config/AccurestSpringMessaging.groovy new file mode 100644 index 0000000000..8604d1e641 --- /dev/null +++ b/samples/messaging-spring/src/test/groovy/io/codearte/accurest/samples/spring/config/AccurestSpringMessaging.groovy @@ -0,0 +1,71 @@ +package io.codearte.accurest.samples.spring.config + +import io.codearte.accurest.messaging.AccurestMessage +import io.codearte.accurest.messaging.AccurestMessageBuilder +import io.codearte.accurest.messaging.AccurestMessaging +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.jms.core.JmsTemplate +import org.springframework.stereotype.Component + +import javax.jms.Message +import java.util.concurrent.TimeUnit +/** + * @author Marcin Grzejszczak + */ +@Component +public class AccurestSpringMessaging implements AccurestMessaging { + + private static final Logger log = LoggerFactory.getLogger(AccurestSpringMessaging.class); + + private final JmsTemplate jmsTemplate; + private final AccurestMessageBuilder builder; + + @Autowired + @SuppressWarnings("unchecked") + public AccurestSpringMessaging(AccurestMessageBuilder builder, JmsTemplate jmsTemplate) { + this.builder = builder + this.jmsTemplate = jmsTemplate + } + + @Override + public void send(AccurestMessage message, String destination) { + try { + jmsTemplate.send(destination, ((SpringMessage) message).messageCreator) + } catch (Exception e) { + log.error("Exception occurred while trying to send a message [" + message + "] " + + "to a channel with name [" + destination + "]", e); + throw e; + } + } + + @Override + @SuppressWarnings("unchecked") + public AccurestMessage receiveMessage(String destination, long timeout, TimeUnit timeUnit) { + try { + return builder.create(jmsTemplate.receive(destination)); + } catch (Exception e) { + log.error("Exception occurred while trying to read a message from " + + " a channel with name [" + destination + "]", e); + throw new RuntimeException(e); + } + } + + @Override + public AccurestMessage receiveMessage(String destination) { + return receiveMessage(destination, 5, TimeUnit.SECONDS); + } + + @Override + @SuppressWarnings("unchecked") + public AccurestMessage create(T t, Map headers) { + return builder.create(t, headers); + } + + @Override + @SuppressWarnings("unchecked") + public AccurestMessage create(Message message) { + return builder.create(message); + } +} diff --git a/samples/messaging-spring/src/test/groovy/io/codearte/accurest/samples/spring/config/ManualAccurestConfiguration.groovy b/samples/messaging-spring/src/test/groovy/io/codearte/accurest/samples/spring/config/ManualAccurestConfiguration.groovy new file mode 100644 index 0000000000..1cd05f86f0 --- /dev/null +++ b/samples/messaging-spring/src/test/groovy/io/codearte/accurest/samples/spring/config/ManualAccurestConfiguration.groovy @@ -0,0 +1,18 @@ +package io.codearte.accurest.samples.spring.config + +import io.codearte.accurest.messaging.AccurestMessaging +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.jms.core.JmsTemplate +/** + * @author Marcin Grzejszczak + */ +@Configuration +class ManualAccurestConfiguration { + + @Bean + AccurestMessaging accurestMessaging(JmsTemplate jmsTemplate) { + return new AccurestSpringMessaging(new AccurestSpringMessageBuilder(), jmsTemplate); + } + +} diff --git a/samples/messaging-spring/src/test/groovy/io/codearte/accurest/samples/spring/config/SpringMessage.groovy b/samples/messaging-spring/src/test/groovy/io/codearte/accurest/samples/spring/config/SpringMessage.groovy new file mode 100644 index 0000000000..1f8f7cc8b6 --- /dev/null +++ b/samples/messaging-spring/src/test/groovy/io/codearte/accurest/samples/spring/config/SpringMessage.groovy @@ -0,0 +1,52 @@ +package io.codearte.accurest.samples.spring.config + +import io.codearte.accurest.messaging.AccurestMessage +import org.springframework.jms.core.MessageCreator + +import javax.jms.Message +import javax.jms.ObjectMessage +/** + * @author Marcin Grzejszczak + */ +public class SpringMessage implements AccurestMessage { + + private final ObjectMessage messageDelegate; + final MessageCreator messageCreator; + + public SpringMessage(Message messageDelegate) { + this.messageDelegate = (ObjectMessage) messageDelegate; + this.messageCreator = null + } + + public SpringMessage(MessageCreator messageCreator) { + this.messageDelegate = null; + this.messageCreator = messageCreator + } + + @Override + public T getPayload() { + return messageDelegate.getObject(); + } + + @Override + public Map getHeaders() { + Map headers = [:] + def enumeration = messageDelegate.propertyNames + while(enumeration.hasMoreElements()) { + String propertyName = enumeration.nextElement() + headers << [(propertyName) : messageDelegate.getObjectProperty(propertyName)] + } + return headers + } + + @Override + Object getHeader(String key) { + return getHeaders().get(key) + } + + @Override + public Message convert() { + return messageDelegate; + } + +} diff --git a/samples/messaging-stream/build.gradle b/samples/messaging-stream/build.gradle new file mode 100644 index 0000000000..69a151237c --- /dev/null +++ b/samples/messaging-stream/build.gradle @@ -0,0 +1,22 @@ +repositories { + mavenLocal() + jcenter() + maven { + url "http://repo.spring.io/snapshot" + } + maven { + url "http://repo.spring.io/milestone" + } +} + +dependencies { + compile project(':accurest-core') + compile 'org.springframework.boot:spring-boot-starter-actuator:1.3.3.RELEASE' + compile 'org.springframework.cloud:spring-cloud-stream-binder-rabbit:1.0.0.RC2' + + testCompile project(':accurest-messaging-root:accurest-messaging-stream') + testCompile 'org.springframework.cloud:spring-cloud-stream-test-support:1.0.0.RC2' + testCompile('org.spockframework:spock-spring:1.0-groovy-2.4') { + exclude(group: 'org.codehaus.groovy') + } +} \ No newline at end of file diff --git a/samples/messaging-stream/src/main/groovy/io/codearte/accurest/samples/messaging/BookDeleted.groovy b/samples/messaging-stream/src/main/groovy/io/codearte/accurest/samples/messaging/BookDeleted.groovy new file mode 100644 index 0000000000..abe8ce0959 --- /dev/null +++ b/samples/messaging-stream/src/main/groovy/io/codearte/accurest/samples/messaging/BookDeleted.groovy @@ -0,0 +1,14 @@ +package io.codearte.accurest.samples.messaging + +import com.fasterxml.jackson.annotation.JsonCreator +import groovy.transform.CompileStatic + +@CompileStatic +class BookDeleted { + final String bookName + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + BookDeleted(String bookName) { + this.bookName = bookName + } +} diff --git a/samples/messaging-stream/src/main/groovy/io/codearte/accurest/samples/messaging/BookListener.groovy b/samples/messaging-stream/src/main/groovy/io/codearte/accurest/samples/messaging/BookListener.groovy new file mode 100644 index 0000000000..fe201e4636 --- /dev/null +++ b/samples/messaging-stream/src/main/groovy/io/codearte/accurest/samples/messaging/BookListener.groovy @@ -0,0 +1,48 @@ +package io.codearte.accurest.samples.messaging + +import groovy.transform.CompileStatic +import groovy.util.logging.Slf4j +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.cloud.stream.annotation.EnableBinding +import org.springframework.cloud.stream.annotation.StreamListener +import org.springframework.cloud.stream.messaging.Sink +import org.springframework.cloud.stream.messaging.Source +import org.springframework.messaging.MessageHeaders +import org.springframework.messaging.support.MessageBuilder + +import java.util.concurrent.atomic.AtomicBoolean + +@CompileStatic +@Slf4j +@EnableBinding([DeleteSink, Sink]) +class BookListener { + + @Autowired Source source + + /** + Scenario for "should generate tests triggered by a message": + client side: if sends a message to input.messageFrom then message will be sent to output.messageFrom + server side: will send a message to input, verify the message contents and await upon receiving message on the output messageFrom + */ + @StreamListener(Sink.INPUT) + void returnBook(BookReturned bookReturned) { + log.info("Returning book [$bookReturned]") + source.output().send(MessageBuilder.createMessage(bookReturned, new MessageHeaders([ + 'BOOK-NAME': bookReturned.bookName as Object + ]))) + } + + /** + Scenario for "should generate tests triggered by a message": + client side: if sends a message to input.messageFrom then message will be sent to output.messageFrom + server side: will send a message to input, verify the message contents and await upon receiving message on the output messageFrom + */ + @StreamListener(DeleteSink.INPUT) + void bookDeleted(BookDeleted bookDeleted) { + log.info("Deleting book [$bookDeleted]") + // ... doing some work + bookSuccessfulyDeleted.set(true) + } + + AtomicBoolean bookSuccessfulyDeleted = new AtomicBoolean(false) +} diff --git a/samples/messaging-stream/src/main/groovy/io/codearte/accurest/samples/messaging/BookReturned.groovy b/samples/messaging-stream/src/main/groovy/io/codearte/accurest/samples/messaging/BookReturned.groovy new file mode 100644 index 0000000000..cca077e266 --- /dev/null +++ b/samples/messaging-stream/src/main/groovy/io/codearte/accurest/samples/messaging/BookReturned.groovy @@ -0,0 +1,14 @@ +package io.codearte.accurest.samples.messaging + +import com.fasterxml.jackson.annotation.JsonCreator +import groovy.transform.CompileStatic + +@CompileStatic +class BookReturned { + final String bookName + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + BookReturned(String bookName) { + this.bookName = bookName + } +} diff --git a/samples/messaging-stream/src/main/groovy/io/codearte/accurest/samples/messaging/BookService.groovy b/samples/messaging-stream/src/main/groovy/io/codearte/accurest/samples/messaging/BookService.groovy new file mode 100644 index 0000000000..ae310cb836 --- /dev/null +++ b/samples/messaging-stream/src/main/groovy/io/codearte/accurest/samples/messaging/BookService.groovy @@ -0,0 +1,36 @@ +package io.codearte.accurest.samples.messaging + +import groovy.transform.CompileStatic +import groovy.util.logging.Slf4j +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.cloud.stream.messaging.Source +import org.springframework.messaging.MessageHeaders +import org.springframework.messaging.support.MessageBuilder +import org.springframework.stereotype.Service + +@Service +@CompileStatic +@Slf4j +class BookService { + + private final Source source + + @Autowired + BookService(Source source) { + this.source = source + } + + /** + Scenario for "should generate tests triggered by a method": + client side: must have a possibility to "trigger" sending of a message to the given messageFrom + server side: will run the method and await upon receiving message on the output messageFrom + + Method triggers sending a message to a source + */ + void returnBook(BookReturned bookReturned) { + log.info("Returning book [$bookReturned]") + source.output().send(MessageBuilder.createMessage(bookReturned, new MessageHeaders([ + 'BOOK-NAME': bookReturned.bookName as Object + ]))) + } +} diff --git a/samples/messaging-stream/src/main/groovy/io/codearte/accurest/samples/messaging/DeleteSink.groovy b/samples/messaging-stream/src/main/groovy/io/codearte/accurest/samples/messaging/DeleteSink.groovy new file mode 100644 index 0000000000..6986da9ca8 --- /dev/null +++ b/samples/messaging-stream/src/main/groovy/io/codearte/accurest/samples/messaging/DeleteSink.groovy @@ -0,0 +1,16 @@ +package io.codearte.accurest.samples.messaging + +import org.springframework.cloud.stream.annotation.Input +import org.springframework.cloud.stream.messaging.Sink +import org.springframework.messaging.SubscribableChannel + +/** + * @author Marcin Grzejszczak + */ +interface DeleteSink extends Sink { + + String INPUT = "delete"; + + @Input(DeleteSink.INPUT) + SubscribableChannel delete(); +} diff --git a/samples/messaging-stream/src/main/groovy/io/codearte/accurest/samples/messaging/StreamMessagingApplication.groovy b/samples/messaging-stream/src/main/groovy/io/codearte/accurest/samples/messaging/StreamMessagingApplication.groovy new file mode 100644 index 0000000000..c6285ea622 --- /dev/null +++ b/samples/messaging-stream/src/main/groovy/io/codearte/accurest/samples/messaging/StreamMessagingApplication.groovy @@ -0,0 +1,16 @@ +package io.codearte.accurest.samples.messaging; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.stream.annotation.EnableBinding +import org.springframework.cloud.stream.messaging.Sink; +import org.springframework.cloud.stream.messaging.Source; + +@SpringBootApplication +@EnableBinding([Source, Sink]) +class StreamMessagingApplication { + + static void main(String[] args) { + SpringApplication.run(StreamMessagingApplication.class, args) + } +} diff --git a/samples/messaging-stream/src/main/resources/application.yml b/samples/messaging-stream/src/main/resources/application.yml new file mode 100644 index 0000000000..1ff1cf7519 --- /dev/null +++ b/samples/messaging-stream/src/main/resources/application.yml @@ -0,0 +1,2 @@ +spring.cloud.stream.bindings.input.destination: inputDestination +spring.cloud.stream.bindings.output.destination: sentTo \ No newline at end of file diff --git a/samples/messaging-stream/src/test/groovy/io/codearte/accurest/samples/messaging/StreamMessagingApplicationSpec.groovy b/samples/messaging-stream/src/test/groovy/io/codearte/accurest/samples/messaging/StreamMessagingApplicationSpec.groovy new file mode 100644 index 0000000000..4cc5d1b2e0 --- /dev/null +++ b/samples/messaging-stream/src/test/groovy/io/codearte/accurest/samples/messaging/StreamMessagingApplicationSpec.groovy @@ -0,0 +1,137 @@ +package io.codearte.accurest.samples.messaging + +import com.fasterxml.jackson.databind.ObjectMapper +import com.jayway.jsonpath.DocumentContext +import com.jayway.jsonpath.JsonPath +import com.toomuchcoding.jsonassert.JsonAssertion +import io.codearte.accurest.dsl.GroovyDsl +import io.codearte.accurest.messaging.AccurestMessage +import io.codearte.accurest.messaging.AccurestMessaging +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.SpringApplicationContextLoader +import org.springframework.test.context.ContextConfiguration +import spock.lang.Specification + +import javax.inject.Inject + +/** + * SPIKE ON TESTS FROM NOTES IN MessagingSpec + */ +// Context configuration would end up in base class +@ContextConfiguration(classes = [StreamMessagingApplication], loader = SpringApplicationContextLoader) +public class StreamMessagingApplicationSpec extends Specification { + + // ALL CASES + @Inject AccurestMessaging accurestMessaging + ObjectMapper accurestObjectMapper = new ObjectMapper() + + def "should work for triggered based messaging"() { + given: + def dsl = GroovyDsl.make { + label 'some_label' + input { + triggeredBy('bookReturnedTriggered()') + } + outputMessage { + sentTo('output') + body('''{ "bookName" : "foo" }''') + headers { + header('BOOK-NAME', 'foo') + } + } + } + // generated test should look like this: + when: + bookReturnedTriggered() + then: + def response = accurestMessaging.receiveMessage('output') + response.headers.get('BOOK-NAME') == 'foo' + and: + DocumentContext parsedJson = JsonPath.parse(accurestObjectMapper.writeValueAsString(response.payload)) + JsonAssertion.assertThat(parsedJson).field('bookName').isEqualTo('foo') + } + + def "should generate tests triggered by a message"() { + given: + def dsl = GroovyDsl.make { + label 'some_label' + input { + messageFrom('input') + messageBody([ + bookName: 'foo' + ]) + messageHeaders { + header('sample', 'header') + } + } + outputMessage { + sentTo('output') + body([ + bookName: 'foo' + ]) + headers { + header('BOOK-NAME', 'foo') + } + } + } + + // generated test should look like this: + + //given: + AccurestMessage inputMessage = accurestMessaging.create( + accurestObjectMapper.writeValueAsString([bookName: 'foo']), + [sample: 'header'] + ) + when: + accurestMessaging.send(inputMessage, 'input') + then: + def response = accurestMessaging.receiveMessage('output') + response.headers.get('BOOK-NAME') == 'foo' + and: + DocumentContext parsedJson = JsonPath.parse(accurestObjectMapper.writeValueAsString(response.payload)) + JsonAssertion.assertThat(parsedJson).field('bookName').isEqualTo('foo') + } + + def "should generate tests without destination, triggered by a message"() { + given: + def dsl = GroovyDsl.make { + label 'some_label' + input { + messageFrom('delete') + messageBody([ + bookName: 'foo' + ]) + messageHeaders { + header('sample', 'header') + } + assertThat('bookWasDeleted()') + } + } + + // generated test should look like this: + + //given: + AccurestMessage inputMessage = accurestMessaging.create( + accurestObjectMapper.writeValueAsString([bookName: 'foo']), + [sample: 'header'] + ) + when: + accurestMessaging.send(inputMessage, 'delete') + then: + noExceptionThrown() + bookWasDeleted() + } + + // BASE CLASS WOULD HAVE THIS: + + @Autowired BookService bookService + @Autowired BookListener bookListener + + void bookReturnedTriggered() { + bookService.returnBook(new BookReturned("foo")) + } + + void bookWasDeleted() { + assert bookListener.bookSuccessfulyDeleted.get() + } +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 6644eb4f03..54053949b1 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,10 +1,21 @@ -include ':docs' +include 'docs' include "accurest-core", "accurest-gradle-plugin", 'accurest-converters', 'accurest-testing-utils' include ':stub-runner:stub-runner' include ':stub-runner:stub-runner-spring' include ':stub-runner:stub-runner-spring-cloud' include ':stub-runner:stub-runner-junit' +include ':accurest-messaging:accurest-messaging-core' +include ':accurest-messaging:accurest-messaging-integration' +include ':accurest-messaging:accurest-messaging-stream' +include ':accurest-messaging:accurest-messaging-camel' + +include ':samples:messaging-stream' +include ':samples:messaging-integration' +include ':samples:messaging-camel' +include ':samples:messaging-spring' + rootProject.name = "accurest" //to prevent StackOverflow in Sonar -project(":stub-runner").name = "stub-runner-root" \ No newline at end of file +project(":stub-runner").name = "stub-runner-root" +project(":accurest-messaging").name = "accurest-messaging-root" \ No newline at end of file diff --git a/stub-runner/stub-runner-junit/build.gradle b/stub-runner/stub-runner-junit/build.gradle index fa14799646..bd9a36bfe0 100644 --- a/stub-runner/stub-runner-junit/build.gradle +++ b/stub-runner/stub-runner-junit/build.gradle @@ -6,7 +6,7 @@ dependencies { compile localGroovy() compile 'junit:junit:4.12' - testCompile('org.spockframework:spock-core:1.0-groovy-2.3') { + testCompile('org.spockframework:spock-core:1.0-groovy-2.4') { exclude(group: 'org.codehaus.groovy') } testCompile 'cglib:cglib-nodep:2.2' diff --git a/stub-runner/stub-runner-spring-cloud/build.gradle b/stub-runner/stub-runner-spring-cloud/build.gradle index a69b8dc7fc..6b394afa9a 100644 --- a/stub-runner/stub-runner-spring-cloud/build.gradle +++ b/stub-runner/stub-runner-spring-cloud/build.gradle @@ -6,7 +6,7 @@ dependencies { compile 'org.springframework.cloud:spring-cloud-starter:1.1.0.RC2' compile 'org.springframework.cloud:spring-cloud-starter-ribbon:1.1.0.RC1' - testCompile('org.spockframework:spock-core:1.0-groovy-2.3') { + testCompile('org.spockframework:spock-core:1.0-groovy-2.4') { exclude(group: 'org.codehaus.groovy') } testCompile 'cglib:cglib-nodep:2.2' @@ -16,7 +16,7 @@ dependencies { testCompile 'io.reactivex:rxjava:1.1.1' testCompile 'org.springframework.boot:spring-boot-starter-test:1.3.3.RELEASE' testCompile "org.springframework:spring-web:4.2.5.RELEASE" - testCompile('org.spockframework:spock-spring:1.0-groovy-2.3') { + testCompile('org.spockframework:spock-spring:1.0-groovy-2.4') { exclude(group: 'org.codehaus.groovy') } testCompile 'ch.qos.logback:logback-classic:1.1.3' diff --git a/stub-runner/stub-runner-spring/build.gradle b/stub-runner/stub-runner-spring/build.gradle index cf735ddc98..f05eb40a0d 100644 --- a/stub-runner/stub-runner-spring/build.gradle +++ b/stub-runner/stub-runner-spring/build.gradle @@ -6,14 +6,14 @@ dependencies { compile localGroovy() compile 'org.springframework:spring-context:[3.0.0.RELEASE,)' - testCompile('org.spockframework:spock-core:1.0-groovy-2.3') { + testCompile('org.spockframework:spock-core:1.0-groovy-2.4') { exclude(group: 'org.codehaus.groovy') } testCompile 'cglib:cglib-nodep:2.2' testCompile 'org.objenesis:objenesis:2.1' testCompile 'org.springframework.boot:spring-boot-starter:1.3.3.RELEASE' testCompile 'org.springframework.boot:spring-boot-starter-test:1.3.3.RELEASE' - testCompile('org.spockframework:spock-spring:1.0-groovy-2.3') { + testCompile('org.spockframework:spock-spring:1.0-groovy-2.4') { exclude(group: 'org.codehaus.groovy') } testCompile 'ch.qos.logback:logback-classic:1.1.3' diff --git a/stub-runner/stub-runner/build.gradle b/stub-runner/stub-runner/build.gradle index 3ee78c7685..d8a1265ef6 100644 --- a/stub-runner/stub-runner/build.gradle +++ b/stub-runner/stub-runner/build.gradle @@ -4,6 +4,7 @@ apply plugin: 'application' mainClassName = 'io.codearte.accurest.stubrunner.StubRunnerMain' dependencies { + compile project(':accurest-core') compile 'org.apache.ivy:ivy:2.4.0' compile localGroovy() compile "com.github.tomakehurst:wiremock:$wiremockVersion" @@ -12,7 +13,7 @@ dependencies { if (rootProject.hasProperty("fatJar")) runtime 'ch.qos.logback:logback-classic:1.1.3' - testCompile('org.spockframework:spock-core:1.0-groovy-2.3') { + testCompile('org.spockframework:spock-core:1.0-groovy-2.4') { exclude(group: 'org.codehaus.groovy') } testCompile 'cglib:cglib-nodep:2.2' diff --git a/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/GroovyDslWrapper.groovy b/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/GroovyDslWrapper.groovy new file mode 100644 index 0000000000..af9af5b155 --- /dev/null +++ b/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/GroovyDslWrapper.groovy @@ -0,0 +1,23 @@ +package io.codearte.accurest.stubrunner + +import groovy.transform.CompileStatic +import groovy.transform.PackageScope +import io.codearte.accurest.dsl.GroovyDsl + +/** + * @author Marcin Grzejszczak + */ +@PackageScope +@CompileStatic +class GroovyDslWrapper { + + @Delegate private final GroovyDsl groovyDsl + + GroovyDslWrapper(GroovyDsl groovyDsl) { + this.groovyDsl = groovyDsl + } + + boolean hasHttpPart() { + return groovyDsl.request + } +} diff --git a/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/StubData.groovy b/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/StubData.groovy new file mode 100644 index 0000000000..93741e2f57 --- /dev/null +++ b/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/StubData.groovy @@ -0,0 +1,13 @@ +package io.codearte.accurest.stubrunner + +import groovy.transform.CompileStatic +import groovy.transform.EqualsAndHashCode +/** + * @author Marcin Grzejszczak + */ +@CompileStatic +@EqualsAndHashCode +class StubData { + final Integer port + final List contracts +} diff --git a/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/StubRepository.groovy b/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/StubRepository.groovy index 147b24b390..f7f43f0f2a 100644 --- a/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/StubRepository.groovy +++ b/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/StubRepository.groovy @@ -2,12 +2,16 @@ package io.codearte.accurest.stubrunner import groovy.transform.CompileStatic import groovy.transform.PackageScope +import groovy.util.logging.Slf4j +import io.codearte.accurest.dsl.GroovyDsl +import org.codehaus.groovy.control.CompilerConfiguration /** * Wraps the folder with WireMock mappings. */ @CompileStatic @PackageScope +@Slf4j class StubRepository { private final File path @@ -19,24 +23,50 @@ class StubRepository { this.path = repository } + Collection getAccurestContracts() { + List contracts = [] + contracts.addAll(accurestDescriptors()) + return contracts + } + /** - * Returns the list of WireMock JSON files wrapped in {@link MappingDescriptor} + * Returns the list of WireMock JSON files wrapped in {@link WiremockMappingDescriptor} */ - List getProjectDescriptors() { - List mappingDescriptors = [] + List getProjectDescriptors() { + List mappingDescriptors = [] mappingDescriptors.addAll(contextDescriptors()) return mappingDescriptors } - private List contextDescriptors() { + private List contextDescriptors() { return path.exists() ? collectMappingDescriptors(path) : [] } - private List collectMappingDescriptors(File descriptorsDirectory) { - List mappingDescriptors = [] + private List collectMappingDescriptors(File descriptorsDirectory) { + List mappingDescriptors = [] descriptorsDirectory.eachFileRecurse { File file -> if (isMappingDescriptor(file)) { - mappingDescriptors << new MappingDescriptor(file) + mappingDescriptors << new WiremockMappingDescriptor(file) + } + } + return mappingDescriptors + } + + private Collection accurestDescriptors() { + return path.exists() ? collectAccurestDescriptors(path) : [] + } + + private Collection collectAccurestDescriptors(File descriptorsDirectory) { + List mappingDescriptors = [] + descriptorsDirectory.eachFileRecurse { File file -> + if (isAccurestDescriptor(file)) { + try { + mappingDescriptors << new GroovyDslWrapper( + ((new GroovyShell(delegate.class.classLoader, new Binding(), new CompilerConfiguration(sourceEncoding:'UTF-8')).evaluate(file)) as GroovyDsl) + ) + } catch (Exception e) { + log.warn("Exception occurred while trying to parse file [$file]", e) + } } } return mappingDescriptors @@ -46,4 +76,9 @@ class StubRepository { return file.isFile() && file.name.endsWith('.json') } + private static boolean isAccurestDescriptor(File file) { + //TODO: Consider script injections implications... + return file.isFile() && file.name.endsWith('.groovy') + } + } diff --git a/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/StubRunner.groovy b/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/StubRunner.groovy index b28238cddc..496e1d49a3 100644 --- a/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/StubRunner.groovy +++ b/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/StubRunner.groovy @@ -2,7 +2,6 @@ package io.codearte.accurest.stubrunner import groovy.transform.CompileStatic import groovy.util.logging.Slf4j - /** * Represents a single instance of ready-to-run stubs. * Can run the stubs and then will return the name of the collaborator together with @@ -14,10 +13,10 @@ import groovy.util.logging.Slf4j @CompileStatic class StubRunner implements StubRunning { - private StubRunnerExecutor localStubRunner private final StubRepository stubRepository - private StubConfiguration stubsConfiguration - private StubRunnerOptions stubRunnerOptions + private final StubConfiguration stubsConfiguration + private final StubRunnerOptions stubRunnerOptions + private StubRunnerExecutor localStubRunner @Deprecated StubRunner(Arguments arguments) { diff --git a/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/StubRunnerExecutor.groovy b/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/StubRunnerExecutor.groovy index a398a3ec4b..67a1b40fee 100644 --- a/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/StubRunnerExecutor.groovy +++ b/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/StubRunnerExecutor.groovy @@ -2,7 +2,6 @@ package io.codearte.accurest.stubrunner import groovy.transform.CompileStatic import groovy.util.logging.Slf4j - /** * Runs stubs for a particular {@link StubServer} */ @@ -57,9 +56,10 @@ class StubRunnerExecutor implements StubFinder { } private void startStubServers(StubConfiguration stubConfiguration, StubRepository repository) { - List mappings = repository.getProjectDescriptors() + List mappings = repository.getProjectDescriptors() + Collection contracts = repository.accurestContracts stubServer = portScanner.tryToExecuteWithFreePort { int availablePort -> - return new StubServer(availablePort, stubConfiguration, mappings).start() + return new StubServer(availablePort, stubConfiguration, mappings, contracts).start() } } diff --git a/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/StubServer.groovy b/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/StubServer.groovy index 830a3a22f5..8fb22a1dd0 100644 --- a/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/StubServer.groovy +++ b/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/StubServer.groovy @@ -13,12 +13,15 @@ import groovy.util.logging.Slf4j class StubServer { private WireMockServer wireMockServer final StubConfiguration stubConfiguration - final Collection mappings + final Collection mappings + final Collection contracts - StubServer(int port, StubConfiguration stubConfiguration, Collection mappings) { + StubServer(int port, StubConfiguration stubConfiguration, Collection mappings, + Collection contracts) { this.stubConfiguration = stubConfiguration this.mappings = mappings this.wireMockServer = new WireMockServer(WireMockConfiguration.wireMockConfig().port(port)) + this.contracts = contracts } StubServer start() { @@ -51,8 +54,8 @@ class StubServer { registerHealthCheck(wireMock, '/health') } - private void registerStubs(Collection sortedMappings, WireMock wireMock) { - sortedMappings.each { MappingDescriptor mappingDescriptor -> + private void registerStubs(Collection sortedMappings, WireMock wireMock) { + sortedMappings.each { WiremockMappingDescriptor mappingDescriptor -> try { wireMock.register(mappingDescriptor.mapping) log.debug("Registered stub mappings from $mappingDescriptor.descriptor") diff --git a/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/MappingDescriptor.groovy b/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/WiremockMappingDescriptor.groovy similarity index 87% rename from stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/MappingDescriptor.groovy rename to stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/WiremockMappingDescriptor.groovy index 18357b82a9..d8cb688f8c 100644 --- a/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/MappingDescriptor.groovy +++ b/stub-runner/stub-runner/src/main/groovy/io/codearte/accurest/stubrunner/WiremockMappingDescriptor.groovy @@ -14,10 +14,10 @@ import groovy.transform.ToString @EqualsAndHashCode @ToString(includePackage = false) @PackageScope -class MappingDescriptor { +class WiremockMappingDescriptor { final File descriptor - MappingDescriptor(File mappingDescriptor) { + WiremockMappingDescriptor(File mappingDescriptor) { this.descriptor = mappingDescriptor } diff --git a/stub-runner/stub-runner/src/test/groovy/io/codearte/accurest/stubrunner/MappingDescriptorSpec.groovy b/stub-runner/stub-runner/src/test/groovy/io/codearte/accurest/stubrunner/MappingDescriptorSpec.groovy index 394cabf2d8..6e04a8beef 100644 --- a/stub-runner/stub-runner/src/test/groovy/io/codearte/accurest/stubrunner/MappingDescriptorSpec.groovy +++ b/stub-runner/stub-runner/src/test/groovy/io/codearte/accurest/stubrunner/MappingDescriptorSpec.groovy @@ -9,7 +9,7 @@ class MappingDescriptorSpec extends Specification { def 'should describe stub mapping'() { given: - MappingDescriptor mappingDescriptor = new MappingDescriptor(MAPPING_DESCRIPTOR) + WiremockMappingDescriptor mappingDescriptor = new WiremockMappingDescriptor(MAPPING_DESCRIPTOR) expect: with(mappingDescriptor.mapping) { diff --git a/stub-runner/stub-runner/src/test/groovy/io/codearte/accurest/stubrunner/StubRepositorySpec.groovy b/stub-runner/stub-runner/src/test/groovy/io/codearte/accurest/stubrunner/StubRepositorySpec.groovy index 337508fce0..8e50f75267 100644 --- a/stub-runner/stub-runner/src/test/groovy/io/codearte/accurest/stubrunner/StubRepositorySpec.groovy +++ b/stub-runner/stub-runner/src/test/groovy/io/codearte/accurest/stubrunner/StubRepositorySpec.groovy @@ -11,7 +11,7 @@ class StubRepositorySpec extends Specification { StubRepository repository = new StubRepository(REPOSITORY_LOCATION) int expectedDescriptorsSize = 8 when: - List descriptors = repository.getProjectDescriptors() + List descriptors = repository.getProjectDescriptors() then: descriptors.size() == expectedDescriptorsSize } @@ -20,7 +20,7 @@ class StubRepositorySpec extends Specification { given: StubRepository repository = new StubRepository(new File('src/test/resources/emptyrepo')) when: - List descriptors = repository.getProjectDescriptors() + List descriptors = repository.getProjectDescriptors() then: descriptors.empty } diff --git a/stub-runner/stub-runner/src/test/groovy/io/codearte/accurest/stubrunner/StubServerSpec.groovy b/stub-runner/stub-runner/src/test/groovy/io/codearte/accurest/stubrunner/StubServerSpec.groovy index 14d71ac9e3..fad9fd82eb 100644 --- a/stub-runner/stub-runner/src/test/groovy/io/codearte/accurest/stubrunner/StubServerSpec.groovy +++ b/stub-runner/stub-runner/src/test/groovy/io/codearte/accurest/stubrunner/StubServerSpec.groovy @@ -11,8 +11,8 @@ class StubServerSpec extends Specification { def 'should register stub mappings upon server start'() { given: - List mappingDescriptors = new StubRepository(repository).getProjectDescriptors() - StubServer pingStubServer = new StubServer(STUB_SERVER_PORT, stubConfiguration, mappingDescriptors) + List mappingDescriptors = new StubRepository(repository).getProjectDescriptors() + StubServer pingStubServer = new StubServer(STUB_SERVER_PORT, stubConfiguration, mappingDescriptors, []) when: pingStubServer.start() then: @@ -23,8 +23,8 @@ class StubServerSpec extends Specification { def 'should provide stub server URL'() { given: - List mappingDescriptors = new StubRepository(repository).getProjectDescriptors() - StubServer pingStubServer = new StubServer(STUB_SERVER_PORT, stubConfiguration, mappingDescriptors) + List mappingDescriptors = new StubRepository(repository).getProjectDescriptors() + StubServer pingStubServer = new StubServer(STUB_SERVER_PORT, stubConfiguration, mappingDescriptors, []) when: pingStubServer.start() then: