Refactored class and method generation (#1116)

following two event storming sessions with @pilloPl and @OlgaMaciaszek and the Spring Cloud Contract community we've decided to refactor the core modules of Spring Cloud Contract Verifier.

The main idea is that the current `JavaTestGenerator`'s implementation no longer uses complex logic to generate classes and methods.

We've introduced abstractions such as:

-    class body builder (to build classes)
-    groovy and java class metadata
-    imports (for various frameworks)
-    default setup for json, xml, restassured, jaxrs
-    class annotations for spock, junit and junit5
-    field insertion e.g. for messaging
This commit is contained in:
Marcin Grzejszczak
2019-06-27 23:01:37 +02:00
committed by GitHub
parent 954ddfb3d5
commit 972a50265f
238 changed files with 14295 additions and 6992 deletions

View File

@@ -1800,7 +1800,7 @@ following code listing shows the `SingleTestGenerator` interface:
[source,groovy]
----
include::{verifier_core_path}/src/main/groovy/org/springframework/cloud/contract/verifier/builder/SingleTestGenerator.groovy[indent=0,lines=17..-1]
include::{verifier_core_path}/src/main/groovy/org/springframework/cloud/contract/verifier/builder/SingleTestGenerator.java[indent=0,lines=17..-1]
----
Again, you must provide a `spring.factories` file, such as the one shown in the following

View File

@@ -16,6 +16,8 @@
package org.springframework.cloud.contract.spec.internal
import java.util.function.Consumer
import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString
import groovy.transform.TypeChecked
@@ -48,6 +50,12 @@ class Cookies {
}
}
void executeForEachCookie(Consumer<Cookie> consumer) {
entries?.each {
cookie -> consumer.accept(cookie)
}
}
DslProperty matching(String value) {
return new DslProperty(value)
}

View File

@@ -16,6 +16,7 @@
package org.springframework.cloud.contract.spec.internal
import java.util.function.Consumer
import java.util.regex.Pattern
import groovy.transform.EqualsAndHashCode
@@ -23,7 +24,6 @@ import groovy.transform.ToString
import groovy.transform.TypeChecked
import groovy.transform.stc.ClosureParams
import groovy.transform.stc.SimpleType
/**
* Represents a set of headers of a request / response or a message
*
@@ -58,6 +58,12 @@ class Headers {
}
}
void executeForEachHeader(Consumer<Header> consumer) {
entries?.each {
header -> consumer.accept(header)
}
}
void headers(Set<Header> headers) {
entries.addAll(headers)
}

View File

@@ -110,15 +110,24 @@ class RegexProperty extends DslProperty implements CanBeDynamic {
}
Object generate() {
String generatedValue = new Xeger(this.pattern.pattern()).generate()
switch (this.clazz) {
case Integer: return Integer.parseInt(generatedValue)
case Double: return Double.parseDouble(generatedValue)
case Float: return Float.parseFloat(generatedValue)
case Long: return Long.parseLong(generatedValue)
case Short: return Short.parseShort(generatedValue)
case Boolean: return Boolean.parseBoolean(generatedValue)
default: return generatedValue
int retries = 3;
try {
String generatedValue = new Xeger(this.pattern.pattern()).generate()
switch (this.clazz) {
case Integer: return Integer.parseInt(generatedValue)
case Double: return Double.parseDouble(generatedValue)
case Float: return Float.parseFloat(generatedValue)
case Long: return Long.parseLong(generatedValue)
case Short: return Short.parseShort(generatedValue)
case Boolean: return Boolean.parseBoolean(generatedValue)
default: return generatedValue
}
} catch(NumberFormatException ex) {
if (retries > 0) {
retries = retries - 1
return generate()
}
throw ex
}
}

View File

@@ -101,6 +101,16 @@ class RecursiveFilesConverterSpec extends Specification {
def "on failure should break processing and throw meaningful exception"() {
given:
def sourceFile = tmpFolder.newFile("test.groovy")
sourceFile.text = """\
org.springframework.cloud.contract.spec.Contract.make {
request {
method GET()
url '/foo'
}
response {
status OK()
}
}"""
and:
def stubGenerator = Stub(StubGenerator)
stubGenerator.canHandleFileName(_) >> { true }

View File

@@ -0,0 +1,3 @@
{
"status": "REQUEST"
}

Binary file not shown.

View File

@@ -210,6 +210,25 @@
<plugin>
<groupId>org.codehaus.gmavenplus</groupId>
<artifactId>gmavenplus-plugin</artifactId>
<!-- Joint compilation -->
<executions>
<execution>
<goals>
<goal>addSources</goal>
<goal>addTestSources</goal>
<goal>generateStubs</goal>
<goal>compile</goal>
<goal>generateTestStubs</goal>
<goal>compileTests</goal>
<goal>removeStubs</goal>
<goal>removeTestStubs</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>

View File

@@ -42,7 +42,6 @@ import static org.springframework.cloud.contract.verifier.util.NamesUtil.beforeL
import static org.springframework.cloud.contract.verifier.util.NamesUtil.convertIllegalPackageChars
import static org.springframework.cloud.contract.verifier.util.NamesUtil.directoryToPackage
import static org.springframework.cloud.contract.verifier.util.NamesUtil.toLastDot
/**
* @author Jakub Kubrynski, codearte.io
*/

View File

@@ -0,0 +1,23 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
interface Acceptor {
boolean accept();
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import java.io.File;
import java.util.Map;
import java.util.Optional;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties;
import org.springframework.cloud.contract.verifier.util.NamesUtil;
import org.springframework.util.StringUtils;
class BaseClassProvider {
private static final Log log = LogFactory.getLog(BaseClassProvider.class);
private static final String SEPARATOR = "_REPLACEME_";
String retrieveBaseClass(ContractVerifierConfigProperties properties,
String includedDirectoryRelativePath) {
String contractPathAsPackage = includedDirectoryRelativePath
.replace(File.separator, ".");
String contractPackage = includedDirectoryRelativePath.replace(File.separator,
SEPARATOR);
// package mapping takes super precedence
if (properties.getBaseClassMappings() != null
&& !properties.getBaseClassMappings().isEmpty()) {
Optional<Map.Entry<String, String>> mapping = properties
.getBaseClassMappings().entrySet().stream().filter(entry -> {
String pattern = entry.getKey();
return contractPathAsPackage.matches(pattern);
}).findFirst();
if (log.isDebugEnabled()) {
log.debug("Matching pattern for contract package ["
+ contractPathAsPackage + "] with setup "
+ properties.getBaseClassMappings() + " is [" + mapping + "]");
}
if (mapping.isPresent()) {
return mapping.get().getValue();
}
}
if (StringUtils.isEmpty(properties.getPackageWithBaseClasses())) {
return properties.getBaseClassForTests();
}
String generatedClassName = generateDefaultBaseClassName(contractPackage,
properties);
return generatedClassName + "Base";
}
private String generateDefaultBaseClassName(String classPackage,
ContractVerifierConfigProperties properties) {
String[] splitPackage = NamesUtil.convertIllegalPackageChars(classPackage)
.split(SEPARATOR);
if (splitPackage.length > 1) {
String last = NamesUtil.capitalize(splitPackage[splitPackage.length - 1]);
String butLast = NamesUtil.capitalize(splitPackage[splitPackage.length - 2]);
return properties.getPackageWithBaseClasses() + "." + butLast + last;
}
return properties.getPackageWithBaseClasses() + "."
+ NamesUtil.capitalize(splitPackage[0]);
}
}

View File

@@ -16,7 +16,7 @@
package org.springframework.cloud.contract.verifier.builder
import groovy.transform.CompileDynamic
import groovy.transform.CompileStatic
import groovy.transform.PackageScope
@@ -33,6 +33,8 @@ class BlockBuilder {
private final StringBuilder builder
private final String spacer
private int indents
private String lineEnding = ""
private String labelPrefix = ""
/**
* @param spacer - char used for spacing
@@ -42,6 +44,34 @@ class BlockBuilder {
builder = new StringBuilder()
}
/**
* Setup line ending
*/
BlockBuilder setupLineEnding(String lineEnding) {
this.lineEnding = lineEnding
return this
}
/**
* Setup label prefix
*/
BlockBuilder setupLabelPrefix(String labelPrefix) {
this.labelPrefix = labelPrefix
return this
}
String getLineEnding() {
return this.lineEnding
}
/**
* Adds indents to start a new block
*/
BlockBuilder appendWithLabelPrefix(String label) {
return append(this.labelPrefix).append(label)
}
/**
* Adds indents to start a new block
*/
@@ -75,8 +105,27 @@ class BlockBuilder {
}
BlockBuilder addLine(String line) {
return addIndented(line).append("\n")
}
BlockBuilder addIndented(String line) {
return addIndentation().append(line)
}
BlockBuilder addIndented(Runnable runnable) {
addIndentation()
builder << "$line\n"
runnable.run()
return this
}
BlockBuilder addLineWithEnding(String line) {
addIndentation()
append(line).addEndingIfNotPresent().addEmptyLine()
return this
}
BlockBuilder addEndingIfNotPresent() {
addAtTheEnd(lineEnding)
return this
}
@@ -85,22 +134,49 @@ class BlockBuilder {
return this
}
@CompileDynamic
private void addIndentation() {
BlockBuilder appendWithSpace(String text) {
return addAtTheEnd(" ").append(text)
}
BlockBuilder appendWithSpace(Runnable runnable) {
addAtTheEnd(" ")
runnable.run()
return this
}
// synactic sugar
BlockBuilder append(Runnable runnable) {
runnable.run()
return this
}
BlockBuilder append(String string) {
builder << string
return this
}
BlockBuilder addIndentation() {
indents.times {
builder << spacer
}
return this
}
@PackageScope
BlockBuilder addBlock(MethodBuilder methodBuilder) {
BlockBuilder inBraces(Runnable runnable) {
builder.append("{\n")
startBlock()
methodBuilder.appendTo(this)
runnable.run()
endBlock()
addEmptyLine()
addAtTheEnd('\n')
addLine("}")
return this
}
boolean endsWith(String text) {
return builder.toString().endsWith(text)
}
/**
* Adds the given text at the end of the line
*
@@ -110,10 +186,23 @@ class BlockBuilder {
String lastChar = builder.charAt(builder.length() - 1) as String
String secondLastChar = builder.length() >= 2 ? builder.
charAt(builder.length() - 2) as String : ""
if (endsWithNewLine(lastChar) && aSpecialSign(secondLastChar, toAdd)) {
boolean isEndWithNewLine = endsWithNewLine(lastChar)
boolean lastCharSpecial = aSpecialSign(lastChar, toAdd)
boolean secondLastCharSpecial = aSpecialSign(secondLastChar, toAdd)
boolean lineEndingToAdd = toAdd == lineEnding
// lastChar = [;] , toAdd = [;]
if (lastChar == toAdd) {
return this
}
else if (endsWithNewLine(lastChar) && !aSpecialSign(secondLastChar, toAdd)) {
// secondLastChar = [ ], lastChar = [{] , toAdd = [;]
else if ((!isEndWithNewLine && lastCharSpecial) && lineEndingToAdd) {
return this
}
// secondLastChar = [{], lastChar = [\n] , toAdd = [;]
else if (isEndWithNewLine && secondLastCharSpecial) {
return this
}
else if (isEndWithNewLine && !secondLastCharSpecial) {
builder.replace(builder.length() - 1, builder.length(), toAdd)
builder << '\n'
}
@@ -131,7 +220,12 @@ class BlockBuilder {
if (!character) {
return false
}
return character == "{" || character == toAdd
return character == "{" ||
(character == spacer && toAdd == spacer) ||
(character == spacer && toAdd == " ") ||
character == toAdd ||
(endsWithNewLine(character) &&
(toAdd == '\n' || toAdd == " " || toAdd == lineEnding))
}
/**

View File

@@ -0,0 +1,130 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import java.util.regex.Pattern;
import org.springframework.cloud.contract.spec.internal.DslProperty;
import org.springframework.cloud.contract.spec.internal.ExecutionProperty;
import org.springframework.cloud.contract.spec.internal.FromFileProperty;
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
class BodyAssertionLineCreator {
private final BlockBuilder blockBuilder;
private final BodyReader bodyReader;
private final String byteArrayString;
private final ComparisonBuilder comparisonBuilder;
BodyAssertionLineCreator(BlockBuilder blockBuilder, GeneratedClassMetaData metaData,
String byteArrayString, ComparisonBuilder comparisonBuilder) {
this.blockBuilder = blockBuilder;
this.bodyReader = new BodyReader(metaData);
this.byteArrayString = byteArrayString;
this.comparisonBuilder = comparisonBuilder;
}
void appendBodyAssertionLine(SingleContractMetadata metadata, String property,
Object value) {
if (value instanceof String && ((String) value).startsWith("$")) {
String newValue = stripFirstChar((String) value).replaceAll("\\$value",
"responseBody" + property);
this.blockBuilder.addLineWithEnding(newValue);
}
else {
this.blockBuilder.addLineWithEnding(
getResponseBodyPropertyComparisonString(metadata, property, value));
}
}
/**
* Builds the code that for the given {@code property} will compare it to the given
* Object {@code value}
*/
private String getResponseBodyPropertyComparisonString(
SingleContractMetadata singleContractMetadata, String property,
Object value) {
if (value instanceof FromFileProperty) {
return getResponseBodyPropertyComparisonString(singleContractMetadata,
property, (FromFileProperty) value);
}
else if (value instanceof Pattern) {
return getResponseBodyPropertyComparisonString(property, (Pattern) value);
}
else if (value instanceof ExecutionProperty) {
return getResponseBodyPropertyComparisonString(property,
(ExecutionProperty) value);
}
else if (value instanceof DslProperty) {
return getResponseBodyPropertyComparisonString(singleContractMetadata,
property, ((DslProperty) value).getServerValue());
}
return getResponseBodyPropertyComparisonString(property, value.toString());
}
/**
* Builds the code that for the given {@code property} will compare it to the given
* byte[] {@code value}
*/
private String getResponseBodyPropertyComparisonString(
SingleContractMetadata singleContractMetadata, String property,
FromFileProperty value) {
if (value.isByte()) {
return this.comparisonBuilder.assertThat(this.byteArrayString)
+ this.comparisonBuilder.isEqualToUnquoted(this.bodyReader
.readBytesFromFileString(singleContractMetadata, value,
CommunicationType.RESPONSE));
}
return getResponseBodyPropertyComparisonString(property, value.asString());
}
/**
* Builds the code that for the given {@code property} will compare it to the given
* String {@code value}
*/
private String getResponseBodyPropertyComparisonString(String property,
String value) {
return this.comparisonBuilder.assertThatUnescaped("responseBody" + property,
value);
}
/**
* Builds the code that for the given {@code property} will match it to the given
* regular expression {@code value}
*/
private String getResponseBodyPropertyComparisonString(String property,
Pattern value) {
return this.comparisonBuilder.assertThat("responseBody" + property, value);
}
/**
* Builds the code that for the given {@code property} will match it to the given
* {@link ExecutionProperty} value
*/
private String getResponseBodyPropertyComparisonString(String property,
ExecutionProperty value) {
return value.insertValue("responseBody" + property);
}
private String stripFirstChar(String s) {
return s.substring(1);
}
}

View File

@@ -1,92 +0,0 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder
import org.apache.commons.text.StringEscapeUtils
import org.springframework.cloud.contract.spec.internal.BodyMatcher
import org.springframework.cloud.contract.spec.internal.MatchingType
import org.springframework.util.SerializationUtils
/**
* @author Marcin Grzejszczak
* @author Olga Maciaszek-Sharma
* @since 2.1.0
*/
trait BodyMethodGeneration {
// Doing a clone doesn't work for nested lists...
Object cloneBody(Object object) {
if (object instanceof List || object instanceof Map) {
byte[] serializedObject = SerializationUtils.serialize(object)
return SerializationUtils.deserialize(serializedObject)
}
try {
return object.clone()
}
catch (CloneNotSupportedException ignored) {
return object
}
}
void addColonIfRequired(Optional<String> lineSuffix, BlockBuilder blockBuilder) {
lineSuffix.ifPresent({
blockBuilder.addAtTheEnd(lineSuffix.get())
})
}
void addBodyMatchingBlock(List<BodyMatcher> matchers, BlockBuilder blockBuilder,
Object responseBody, boolean shouldCommentOutBDDBlocks) {
blockBuilder.endBlock()
blockBuilder.addLine(getAssertionJoiner(shouldCommentOutBDDBlocks))
blockBuilder.startBlock()
matchers.each {
if (it.matchingType() == MatchingType.NULL) {
methodForNullCheck(it, blockBuilder)
}
else if (MatchingType.regexRelated(it.matchingType())
|| it
.matchingType()
== MatchingType.EQUALITY) {
methodForEqualityCheck(it, blockBuilder, responseBody)
}
else if (it.matchingType() == MatchingType.COMMAND) {
methodForCommandExecution(it, blockBuilder, responseBody)
}
else {
methodForTypeCheck(it, blockBuilder, responseBody)
}
}
}
String quotedAndEscaped(String string) {
return '"' + StringEscapeUtils.escapeJava(string) + '"'
}
abstract void methodForNullCheck(BodyMatcher bodyMatcher, BlockBuilder bb)
abstract void methodForEqualityCheck(BodyMatcher bodyMatcher, BlockBuilder bb, Object body)
abstract void methodForCommandExecution(BodyMatcher bodyMatcher, BlockBuilder bb, Object body)
abstract void methodForTypeCheck(BodyMatcher bodyMatcher, BlockBuilder bb, Object body)
String getAssertionJoiner(boolean shouldCommentOutBDDBlocks) {
return shouldCommentOutBDDBlocks ? '// and:' : 'and:'
}
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import java.util.List;
import java.util.Optional;
import org.apache.commons.text.StringEscapeUtils;
import org.springframework.cloud.contract.spec.internal.BodyMatcher;
import org.springframework.cloud.contract.spec.internal.MatchingType;
import org.springframework.util.SerializationUtils;
/**
* @author Marcin Grzejszczak
* @author Olga Maciaszek-Sharma
* @since 2.1.0
*/
interface BodyMethodGeneration {
default Object cloneBody(Object object) {
byte[] serializedObject = SerializationUtils.serialize(object);
return SerializationUtils.deserialize(serializedObject);
}
default void addColonIfRequired(Optional<String> lineSuffix,
BlockBuilder blockBuilder) {
lineSuffix.ifPresent(s -> blockBuilder.addAtTheEnd(lineSuffix.get()));
}
default void addBodyMatchingBlock(List<BodyMatcher> matchers,
BlockBuilder blockBuilder, Object responseBody,
boolean shouldCommentOutBDDBlocks) {
blockBuilder.endBlock();
blockBuilder.addLine(getAssertionJoiner(shouldCommentOutBDDBlocks));
blockBuilder.startBlock();
matchers.forEach(it -> {
if (it.matchingType() == MatchingType.NULL) {
methodForNullCheck(it, blockBuilder);
}
else if (MatchingType.regexRelated(it.matchingType())
|| it.matchingType() == MatchingType.EQUALITY) {
methodForEqualityCheck(it, blockBuilder, responseBody);
}
else if (it.matchingType() == MatchingType.COMMAND) {
methodForCommandExecution(it, blockBuilder, responseBody);
}
else {
methodForTypeCheck(it, blockBuilder, responseBody);
}
});
}
default String quotedAndEscaped(String string) {
return '"' + StringEscapeUtils.escapeJava(string) + '"';
}
void methodForNullCheck(BodyMatcher bodyMatcher, BlockBuilder bb);
void methodForEqualityCheck(BodyMatcher bodyMatcher, BlockBuilder bb, Object body);
void methodForCommandExecution(BodyMatcher bodyMatcher, BlockBuilder bb, Object body);
void methodForTypeCheck(BodyMatcher bodyMatcher, BlockBuilder bb, Object body);
default String getAssertionJoiner(boolean shouldCommentOutBDDBlocks) {
return shouldCommentOutBDDBlocks ? "// and:" : "and:";
}
}

View File

@@ -0,0 +1,142 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
/**
* Adds a label, proper indents and line endings for the body of a method.
*/
interface BodyMethodVisitor {
/**
* Adds a starting body method block. E.g. //given: together with all indents
* @param blockBuilder
* @param label
* @return
*/
default BlockBuilder startBodyBlock(BlockBuilder blockBuilder, String label) {
return blockBuilder.addIndentation().appendWithLabelPrefix(label).addEmptyLine()
.startBlock();
}
/**
* Picks matching elements, visits them and applies indents.
* @param blockBuilder
* @param methodVisitors
* @param singleContractMetadata
*/
default void indentedBodyBlock(BlockBuilder blockBuilder,
List<? extends MethodVisitor> methodVisitors,
SingleContractMetadata singleContractMetadata) {
List<MethodVisitor> visitors = filterVisitors(methodVisitors,
singleContractMetadata);
if (visitors.isEmpty()) {
blockBuilder.addEndingIfNotPresent().addEmptyLine();
blockBuilder.endBlock();
return;
}
blockBuilder.addEmptyLine().indent();
applyVisitors(blockBuilder, singleContractMetadata, visitors);
endIndentedBodyBlock(blockBuilder);
}
/**
* Picks matching visitors.
* @param methodVisitors
* @param singleContractMetadata
* @return
*/
default List<MethodVisitor> filterVisitors(
List<? extends MethodVisitor> methodVisitors,
SingleContractMetadata singleContractMetadata) {
return methodVisitors.stream()
.filter(given -> given.accept(singleContractMetadata))
.collect(Collectors.toList());
}
/**
* Picks matching elements, visits them. Doesn't apply indents. Useful for the //
* then: block where there is no method chaining.
* @param blockBuilder
* @param methodVisitors
* @param singleContractMetadata
*/
default void bodyBlock(BlockBuilder blockBuilder,
List<? extends MethodVisitor> methodVisitors,
SingleContractMetadata singleContractMetadata) {
List<MethodVisitor> visitors = filterVisitors(methodVisitors,
singleContractMetadata);
if (visitors.isEmpty()) {
blockBuilder.addEndingIfNotPresent().addEmptyLine();
return;
}
applyVisitorsWithEnding(blockBuilder, singleContractMetadata, visitors);
endBodyBlock(blockBuilder);
}
/**
* Executes logic for all the matching visitors.
* @param blockBuilder
* @param singleContractMetadata
* @param visitors
*/
default void applyVisitors(BlockBuilder blockBuilder,
SingleContractMetadata singleContractMetadata, List<MethodVisitor> visitors) {
Iterator<MethodVisitor> iterator = visitors.iterator();
while (iterator.hasNext()) {
MethodVisitor visitor = iterator.next();
visitor.apply(singleContractMetadata);
if (iterator.hasNext()) {
blockBuilder.addEmptyLine();
}
}
blockBuilder.addEndingIfNotPresent();
}
/**
* Executes logic for all the matching visitors.
* @param blockBuilder
* @param singleContractMetadata
* @param visitors
*/
default void applyVisitorsWithEnding(BlockBuilder blockBuilder,
SingleContractMetadata singleContractMetadata, List<MethodVisitor> visitors) {
Iterator<MethodVisitor> iterator = visitors.iterator();
while (iterator.hasNext()) {
MethodVisitor visitor = iterator.next();
visitor.apply(singleContractMetadata);
blockBuilder.addEndingIfNotPresent();
if (iterator.hasNext()) {
blockBuilder.addEmptyLine();
}
}
}
default void endIndentedBodyBlock(BlockBuilder blockBuilder) {
blockBuilder.addEndingIfNotPresent().unindent().endBlock();
}
default void endBodyBlock(BlockBuilder blockBuilder) {
blockBuilder.addEndingIfNotPresent().endBlock();
}
}

View File

@@ -0,0 +1,164 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import groovy.json.JsonOutput;
import groovy.lang.Closure;
import groovy.lang.GString;
import org.apache.commons.text.StringEscapeUtils;
import org.springframework.cloud.contract.spec.internal.DslProperty;
import org.springframework.cloud.contract.spec.internal.FromFileProperty;
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
import org.springframework.cloud.contract.verifier.util.ContentType;
import org.springframework.cloud.contract.verifier.util.ContentUtils;
import org.springframework.cloud.contract.verifier.util.MapConverter;
import static org.apache.commons.text.StringEscapeUtils.escapeJava;
import static org.springframework.cloud.contract.verifier.util.ContentType.DEFINED;
import static org.springframework.cloud.contract.verifier.util.ContentType.FORM;
import static org.springframework.cloud.contract.verifier.util.ContentType.JSON;
import static org.springframework.cloud.contract.verifier.util.ContentType.TEXT;
import static org.springframework.cloud.contract.verifier.util.ContentUtils.extractValue;
interface BodyParser extends BodyThen {
String byteArrayString();
default String convertUnicodeEscapesIfRequired(String json) {
String unescapedJson = StringEscapeUtils.unescapeEcmaScript(json);
return escapeJava(unescapedJson);
}
default String convertToJsonString(Object bodyValue) {
String json = JsonOutput.toJson(bodyValue);
json = convertUnicodeEscapesIfRequired(json);
return trimRepeatedQuotes(json);
}
default String trimRepeatedQuotes(String toTrim) {
if (toTrim.startsWith("\"")) {
return toTrim.replaceAll("\"", "");
// #261
}
else if (toTrim.startsWith("\\\"") && toTrim.endsWith("\\\"")) {
return toTrim.substring(2, toTrim.length() - 2);
}
return toTrim;
}
default Object convertResponseBody(SingleContractMetadata metadata) {
ContentType contentType = metadata.getOutputTestContentType();
DslProperty body = responseBody(metadata);
Object responseBody = extractServerValueFromBody(contentType,
body.getServerValue());
if (responseBody instanceof FromFileProperty) {
responseBody = ((FromFileProperty) responseBody).asString();
}
else if (responseBody instanceof GString) {
responseBody = extractValue((GString) responseBody, contentType,
o -> o instanceof DslProperty ? ((DslProperty) o).getServerValue()
: o);
}
else if (responseBody instanceof DslProperty) {
responseBody = MapConverter.getTestSideValues(responseBody);
}
return responseBody;
}
String responseAsString();
@SuppressWarnings("unchecked")
default String requestBodyAsString(SingleContractMetadata metadata) {
ContentType contentType = metadata.getInputTestContentType();
DslProperty body = requestBody(metadata);
Object bodyValue = extractServerValueFromBody(contentType, body.getServerValue());
if (contentType == ContentType.FORM) {
if (bodyValue instanceof Map) {
// [a:3, b:4] == "a=3&b=4"
return ((Map) bodyValue).entrySet().stream().map(o -> {
Map.Entry entry = (Map.Entry) o;
return convertUnicodeEscapesIfRequired(entry.getKey().toString() + "="
+ MapConverter.getTestSideValuesForText(entry.getValue()));
}).collect(Collectors.joining("&")).toString();
}
else if (bodyValue instanceof List) {
// ["a=3", "b=4"] == "a=3&b=4"
return ((List) bodyValue).stream()
.map(o -> convertUnicodeEscapesIfRequired(
MapConverter.getTestSideValuesForText(o).toString()))
.collect(Collectors.joining("&")).toString();
}
}
else {
return convertToJsonString(bodyValue);
}
return "";
}
/**
* Converts the passed body into ints server side representation. All
* {@link DslProperty} will return their server side values
*/
default Object extractServerValueFromBody(ContentType contentType, Object bodyValue) {
if (bodyValue instanceof GString) {
return extractValue((GString) bodyValue, contentType,
ContentUtils.GET_TEST_SIDE);
}
if (TEXT != contentType && FORM != contentType && DEFINED != contentType) {
boolean dontParseStrings = contentType == JSON && bodyValue instanceof Map;
Closure parsingClosure = dontParseStrings ? Closure.IDENTITY
: MapConverter.JSON_PARSING_CLOSURE;
return MapConverter.transformValues(bodyValue, ContentUtils.GET_TEST_SIDE,
parsingClosure);
}
return bodyValue;
}
default String escape(String text) {
return StringEscapeUtils.escapeJava(text);
}
default String escapeForSimpleTextAssertion(String text) {
return text;
}
default String postProcessJsonPath(String jsonPath) {
return jsonPath;
}
default String quotedLongText(Object text) {
return quotedEscapedLongText(escape(text.toString()));
}
default String quotedEscapedLongText(Object text) {
return "\"" + text.toString() + "\"";
}
default String quotedShortText(Object text) {
return quotedLongText(text);
}
default String quotedEscapedShortText(Object text) {
return quotedEscapedLongText(text);
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import org.springframework.cloud.contract.spec.internal.FromFileProperty;
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
import org.springframework.util.Assert;
class BodyReader {
private final GeneratedClassMetaData generatedClassMetaData;
BodyReader(GeneratedClassMetaData generatedClassMetaData) {
this.generatedClassMetaData = generatedClassMetaData;
}
String readBytesFromFileString(SingleContractMetadata metadata,
FromFileProperty property, CommunicationType side) {
String fileName = byteBodyToAFileForTestMethod(metadata, property, side);
return "fileToBytes(this, \"" + fileName + "\")";
}
String readStringFromFileString(SingleContractMetadata metadata,
FromFileProperty property, CommunicationType side) {
return "new String(" + readBytesFromFileString(metadata, property, side) + ")";
}
private String byteBodyToAFileForTestMethod(SingleContractMetadata metadata,
FromFileProperty property, CommunicationType side) {
GeneratedClassDataForMethod classDataForMethod = new GeneratedClassDataForMethod(
this.generatedClassMetaData.generatedClassData, metadata.methodName());
String newFileName = classDataForMethod.getMethodName() + "_"
+ side.name().toLowerCase() + "_" + property.fileName();
java.nio.file.Path parent = classDataForMethod.testClassPath().getParent();
if (parent == null) {
parent = classDataForMethod.testClassPath();
}
File newFile = new File(parent.toFile(), newFileName);
// for IDE
try {
Files.write(newFile.toPath(), property.asBytes());
// for plugin
generatedTestResourcesFileBytes(property, newFile);
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
return newFileName;
}
private void generatedTestResourcesFileBytes(FromFileProperty property, File newFile)
throws IOException {
Assert.notNull(
this.generatedClassMetaData.configProperties.getGeneratedTestSourcesDir(),
"No generated test sources directory set");
Assert.notNull(
this.generatedClassMetaData.configProperties
.getGeneratedTestResourcesDir(),
"No generated test resources directory set");
Path path = this.generatedClassMetaData.configProperties
.getGeneratedTestSourcesDir().toPath();
Path relativePath = path.relativize(newFile.toPath());
File newFileInGeneratedTestSources = new File(
this.generatedClassMetaData.configProperties
.getGeneratedTestResourcesDir(),
relativePath.toString());
newFileInGeneratedTestSources.getParentFile().mkdirs();
Files.write(newFileInGeneratedTestSources.toPath(), property.asBytes());
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import org.springframework.cloud.contract.spec.internal.BodyMatchers;
import org.springframework.cloud.contract.spec.internal.DslProperty;
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
interface BodyThen {
default DslProperty requestBody(SingleContractMetadata metadata) {
return metadata.getContract().getRequest().getBody();
}
default DslProperty responseBody(SingleContractMetadata metadata) {
return metadata.getContract().getResponse().getBody();
}
default BodyMatchers responseBodyMatchers(SingleContractMetadata metadata) {
return metadata.getContract().getResponse().getBodyMatchers();
}
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
interface ClassAnnotation extends Visitor<ClassAnnotation> {
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
class ClassAnnotationsBuilder {
private final GeneratedTestClassBuilder parentBuilder;
private final BlockBuilder builder;
private final GeneratedClassMetaData metaData;
ClassAnnotationsBuilder(GeneratedTestClassBuilder generatedTestClassBuilder) {
this.parentBuilder = generatedTestClassBuilder;
this.builder = generatedTestClassBuilder.blockBuilder;
this.metaData = generatedTestClassBuilder.generatedClassMetaData;
}
ClassAnnotationsBuilder jUnit4() {
this.parentBuilder
.classAnnotations(new JUnit4OrderClassAnnotation(builder, metaData));
return this;
}
ClassAnnotationsBuilder jUnit5() {
this.parentBuilder
.classAnnotations(new JUnit5OrderClassAnnotation(builder, metaData));
return this;
}
ClassAnnotationsBuilder spock() {
this.parentBuilder
.classAnnotations(new SpockOrderClassAnnotation(builder, metaData));
return this;
}
GeneratedTestClassBuilder build() {
return this.parentBuilder;
}
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
/**
* Builds the body of the class. Sets fields, methods.
*
* @author Olga Maciaszek-Sharma
* @author Marcin Grzejszczak
* @since 2.2.0
*/
class ClassBodyBuilder {
private List<Field> fields = new LinkedList<>();
private SingleMethodBuilder methodBuilder;
final BlockBuilder blockBuilder;
final GeneratedClassMetaData generatedClassMetaData;
private ClassBodyBuilder(BlockBuilder blockBuilder,
GeneratedClassMetaData generatedClassMetaData) {
this.blockBuilder = blockBuilder;
this.generatedClassMetaData = generatedClassMetaData;
}
static ClassBodyBuilder builder(BlockBuilder blockBuilder,
GeneratedClassMetaData generatedClassMetaData) {
return new ClassBodyBuilder(blockBuilder, generatedClassMetaData);
}
FieldBuilder field() {
return new FieldBuilder(this);
}
ClassBodyBuilder field(Field field) {
this.fields.add(field);
return this;
}
ClassBodyBuilder methodBuilder(SingleMethodBuilder methodBuilder) {
this.methodBuilder = methodBuilder;
return this;
}
/**
* Mutates the {@link BlockBuilder} to generate methods and fields.
* @return block builder with contents of built methods
*/
BlockBuilder build() {
this.blockBuilder.inBraces(() -> {
// @Rule ...
visit(this.fields);
// new line if fields added
this.methodBuilder.build();
});
return this.blockBuilder;
}
void visit(List<? extends Visitor> list) {
List<? extends Visitor> visitors = list.stream().filter(Acceptor::accept)
.collect(Collectors.toList());
Iterator<? extends Visitor> iterator = visitors.iterator();
while (iterator.hasNext()) {
Visitor visitor = iterator.next();
visitor.call();
this.blockBuilder.addEndingIfNotPresent();
if (iterator.hasNext()) {
this.blockBuilder.addEmptyLine();
}
}
}
}

View File

@@ -1,232 +0,0 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder
import groovy.transform.CompileStatic
import groovy.transform.PackageScope
import org.apache.commons.logging.Log
import org.apache.commons.logging.LogFactory
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
import org.springframework.cloud.contract.verifier.config.TestFramework
import org.springframework.cloud.contract.verifier.util.NamesUtil
/**
* Builds a class. Adds all the imports, static imports etc.
*
* @author Jakub Kubrynski, codearte.io
*
* @since 1.0.0
*/
@CompileStatic
@PackageScope
class ClassBuilder {
private static final Log log = LogFactory.getLog(ClassBuilder)
private static final String SEPARATOR = "_REPLACEME_"
private final String className
private final String packageName
private final String baseClass
private final List<String> imports = []
private final List<String> staticImports = []
private final List<String> rules = []
private final List<String> fields = []
private final List<MethodBuilder> methods = []
private final List<String> classLevelAnnotations = []
private final TestFramework lang
private ClassBuilder(String className, String packageName, String baseClass, TestFramework lang) {
this.lang = lang
if (baseClass) {
imports << baseClass
}
this.baseClass = NamesUtil.afterLastDot(baseClass)
this.packageName = packageName
this.className = className
}
/**
* @return a{@link ClassBuilder} for the given parameters
*/
static ClassBuilder createClass(String className, String classPackage, ContractVerifierConfigProperties properties,
String includedDirectoryRelativePath) {
String baseClassForTests
if (properties.testFramework == TestFramework.SPOCK && !properties.baseClassForTests
&& !properties.packageWithBaseClasses
&& !properties.baseClassMappings) {
baseClassForTests = 'spock.lang.Specification'
}
else {
baseClassForTests =
retrieveBaseClass(properties, includedDirectoryRelativePath)
}
return new ClassBuilder(className, classPackage, baseClassForTests, properties.testFramework)
}
protected static String retrieveBaseClass(ContractVerifierConfigProperties properties, String includedDirectoryRelativePath) {
String contractPathAsPackage = includedDirectoryRelativePath.
replace(File.separator, ".")
String contractPackage = includedDirectoryRelativePath.
replace(File.separator, SEPARATOR)
// package mapping takes super precedence
if (properties.baseClassMappings) {
Map.Entry<String, String> mapping = properties.baseClassMappings.
find { String pattern, String fqn ->
return contractPathAsPackage.matches(pattern)
}
if (log.isDebugEnabled()) {
log.debug("Matching pattern for contract package [${contractPathAsPackage}] with setup ${properties.baseClassMappings} is [${mapping}]")
}
if (mapping) {
return mapping.value
}
}
if (!properties.packageWithBaseClasses) {
return properties.baseClassForTests
}
String generatedClassName =
generateDefaultBaseClassName(contractPackage, properties)
return "${generatedClassName}Base"
}
private static String generateDefaultBaseClassName(String classPackage, ContractVerifierConfigProperties properties) {
String[] splitPackage = NamesUtil.convertIllegalPackageChars(classPackage).
split(SEPARATOR)
if (splitPackage.size() > 1) {
String last = NamesUtil.capitalize(splitPackage[-1])
String butLast = NamesUtil.capitalize(splitPackage[-2])
return "${properties.packageWithBaseClasses}.${butLast}${last}"
}
return "${properties.packageWithBaseClasses}.${NamesUtil.capitalize(splitPackage[0])}"
}
ClassBuilder addImport(String importToAdd) {
imports << importToAdd
return this
}
ClassBuilder addImports(List<String> importsToAdd) {
imports.addAll(importsToAdd)
return this
}
ClassBuilder addStaticImports(List<String> importsToAdd) {
staticImports.addAll(importsToAdd)
return this
}
ClassBuilder addStaticImport(String importToAdd) {
staticImports << importToAdd
return this
}
ClassBuilder addMethod(MethodBuilder methodBuilder) {
methods << methodBuilder
return this
}
ClassBuilder addField(String fieldToAdd) {
fields << appendColonIfJUniTest(fieldToAdd)
return this
}
ClassBuilder addField(List<String> fieldsToAdd) {
fields.addAll(fieldsToAdd.collect { appendColonIfJUniTest(it) })
return this
}
ClassBuilder addRule(String ruleClass) {
imports << ruleClass
rules << NamesUtil.afterLastDot(ruleClass)
return this
}
String build() {
BlockBuilder clazz = new BlockBuilder("\t")
.addLine("package $packageName$lang.lineSuffix")
.addEmptyLine()
imports.sort().each {
clazz.addLine("import $it$lang.lineSuffix")
}
if (!imports.empty) {
clazz.addEmptyLine()
}
staticImports.sort().each {
clazz.addLine("import static $it$lang.lineSuffix")
}
if (!staticImports.empty) {
clazz.addEmptyLine()
}
classLevelAnnotations.sort().each {
clazz.addLine(it)
}
def classLine = "${lang.classModifier}class $className"
if (baseClass) {
classLine += " extends $baseClass"
}
clazz.addLine(classLine + ' {')
clazz.addEmptyLine()
clazz.startBlock()
rules.sort().each {
clazz.addLine("@Rule")
clazz.
addLine("public $it ${NamesUtil.camelCase(it)} = new $it()$lang.lineSuffix")
}
clazz.endBlock()
if (!rules.empty) {
clazz.addEmptyLine()
}
clazz.startBlock()
fields.sort().each {
clazz.addLine(it)
}
if (!fields.empty) {
clazz.addEmptyLine()
}
clazz.endBlock()
methods.each {
clazz.addBlock(it)
}
clazz.addLine('}')
clazz.toString()
}
void addClassLevelAnnotation(String annotation) {
classLevelAnnotations << annotation
}
private String appendColonIfJUniTest(String field) {
if (isJUnitType(field)) {
return "$field;"
}
return field
}
private boolean isJUnitType(String field) {
TestFramework.JUNIT == lang || TestFramework.JUNIT5 == lang && !field.endsWith(';')
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
interface ClassMetaData extends Acceptor {
ClassMetaData setupLineEnding();
ClassMetaData setupLabelPrefix();
ClassMetaData packageDefinition();
ClassMetaData modifier();
ClassMetaData suffix();
ClassMetaData parentClass();
ClassMetaData className();
}

View File

@@ -0,0 +1,30 @@
package org.springframework.cloud.contract.verifier.builder;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Used to return the {@link Class} against which the type of the element should be
* verified using <code>instanceof</code> in generated response assertions.
*
* @author Marcin Grzejszczak
* @author Olga Maciaszek-Sharma
* @since 2.1.0
*/
public interface ClassVerifier {
default Class classToCheck(Object elementFromBody) {
if (elementFromBody instanceof List) {
return List.class;
}
else if (elementFromBody instanceof Set) {
return Set.class;
}
else if (elementFromBody instanceof Map) {
return Map.class;
}
return elementFromBody.getClass();
}
}

View File

@@ -14,9 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder
import groovy.transform.CompileStatic
package org.springframework.cloud.contract.verifier.builder;
/**
* Describes the type of communication
@@ -24,7 +22,8 @@ import groovy.transform.CompileStatic
* @author Marcin Grzejszczak
* @since 2.1.0
*/
@CompileStatic
enum CommunicationType {
REQUEST, RESPONSE, INPUT, OUTPUT
public enum CommunicationType {
REQUEST, RESPONSE, INPUT, OUTPUT;
}

View File

@@ -0,0 +1,104 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import java.util.regex.Pattern;
import org.apache.commons.text.StringEscapeUtils;
import static org.apache.commons.text.StringEscapeUtils.escapeJava;
interface ComparisonBuilder {
ComparisonBuilder JAVA_HTTP_INSTANCE = () -> RestAssuredBodyParser.INSTANCE;
ComparisonBuilder JAVA_MESSAGING_INSTANCE = () -> JavaMessagingBodyParser.INSTANCE;
default String createComparison(Object headerValue) {
if (headerValue instanceof Pattern) {
return matches((Pattern) headerValue);
}
else if (headerValue instanceof Number) {
return isEqualTo((Number) headerValue);
}
String escapedHeader = convertUnicodeEscapesIfRequired(headerValue.toString());
return isEqualTo(escapedHeader);
}
default String createUnescapedComparison(Object headerValue) {
if (headerValue instanceof Pattern) {
return createComparison((Pattern) headerValue);
}
else if (headerValue instanceof Number) {
return isEqualTo((Number) headerValue);
}
return isEqualTo(headerValue.toString());
}
default String assertThat(String object) {
return "assertThat(" + object + ")";
}
default String assertThatIsNotNull(String object) {
return assertThat(object) + isNotNull();
}
default String assertThat(String object, Object valueToCompareAgainst) {
return assertThat(object) + createComparison(valueToCompareAgainst);
}
default String assertThatUnescaped(String object, Object valueToCompareAgainst) {
return assertThat(object) + createUnescapedComparison(valueToCompareAgainst);
}
default String isEqualTo(String escapedHeaderValue) {
return isEqualToUnquoted(bodyParser().quotedShortText(escapedHeaderValue));
}
default String isEqualToUnquoted(String unquoted) {
return ".isEqualTo(" + unquoted + ")";
}
default String isEqualTo(Number number) {
return ".isEqualTo(" + number.toString() + ")";
}
default String isNotNull() {
return ".isNotNull()";
}
default String matches(Pattern pattern) {
String escapedPattern = StringEscapeUtils.escapeJava(pattern.pattern());
return ".matches(" + bodyParser().quotedEscapedShortText(escapedPattern) + ")";
}
default String matches(String pattern) {
return ".matches(" + bodyParser().quotedShortText(pattern) + ")";
}
default String matchesEscaped(String pattern) {
return ".matches(" + bodyParser().quotedEscapedShortText(pattern) + ")";
}
default String convertUnicodeEscapesIfRequired(String json) {
String unescapedJson = StringEscapeUtils.unescapeJson(json);
return escapeJava(unescapedJson);
}
BodyParser bodyParser();
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import org.apache.commons.text.StringEscapeUtils;
import org.springframework.cloud.contract.spec.internal.ExecutionProperty;
import org.springframework.cloud.contract.verifier.util.MapConverter;
final class ContentHelper {
/**
* Depending on the object type extracts the test side values and combines them into a
* String representation. Will not try to guess the type of the value of the header
* (e.g. if it's a JSON).
*/
static String getTestSideForNonBodyValue(Object object) {
if (object instanceof ExecutionProperty) {
return getTestSideValue(object);
}
return quotedAndEscaped(
MapConverter.getTestSideValuesForNonBody(object).toString());
}
/**
* Depending on the object type extracts the test side values and combines them into a
* String representation
*/
private static String getTestSideValue(Object object) {
if (object instanceof ExecutionProperty) {
return object.toString();
}
return '"' + MapConverter.getTestSideValues(object).toString() + '"';
}
private static String quotedAndEscaped(String string) {
return '"' + StringEscapeUtils.escapeJava(string) + '"';
}
}

View File

@@ -0,0 +1,85 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import java.util.regex.Pattern;
import org.springframework.cloud.contract.spec.internal.Cookies;
import org.springframework.cloud.contract.spec.internal.ExecutionProperty;
import org.springframework.cloud.contract.spec.internal.NotToEscapePattern;
import org.springframework.cloud.contract.spec.internal.Response;
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
import org.springframework.cloud.contract.verifier.util.MapConverter;
interface CookieElementProcessor {
ComparisonBuilder comparisonBuilder();
default void processCookies(SingleContractMetadata metadata) {
Response response = metadata.getContract().getResponse();
Cookies cookies = response.getCookies();
cookies.executeForEachCookie(cookie -> processCookieElement(cookie.getKey(),
cookie.getServerValue() instanceof NotToEscapePattern
? cookie.getServerValue()
: MapConverter.getTestSideValues(cookie.getServerValue())));
blockBuilder().addEndingIfNotPresent();
}
BlockBuilder blockBuilder();
default void processCookieElement(String property, Object value) {
if (value instanceof NotToEscapePattern) {
verifyCookieNotNull(property);
blockBuilder()
.addIndented(comparisonBuilder().assertThat(cookieValue(property))
+ comparisonBuilder().matches(((NotToEscapePattern) value)
.getServerValue().pattern().replace("\\", "\\\\")));
}
else if (value instanceof String || value instanceof Pattern) {
verifyCookieNotNull(property);
blockBuilder().addIndented(
comparisonBuilder().assertThat(cookieValue(property), value));
}
else if (value instanceof Number) {
verifyCookieNotNull(property);
blockBuilder().addIndented(
comparisonBuilder().assertThat(cookieValue(property), value));
}
else if (value instanceof ExecutionProperty) {
verifyCookieNotNull(property);
blockBuilder().addIndented(
((ExecutionProperty) value).insertValue(cookieValue(property)));
}
else {
// fallback
processCookieElement(property, value.toString());
}
}
default void verifyCookieNotNull(String key) {
blockBuilder().addLineWithEnding(
comparisonBuilder().assertThatIsNotNull(cookieKey(key)));
}
String cookieKey(String key);
default String cookieValue(String key) {
return cookieKey(key);
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import java.util.Arrays;
class CustomImports implements Imports {
private final BlockBuilder blockBuilder;
private final GeneratedClassMetaData generatedClassMetaData;
CustomImports(BlockBuilder blockBuilder,
GeneratedClassMetaData generatedClassMetaData) {
this.blockBuilder = blockBuilder;
this.generatedClassMetaData = generatedClassMetaData;
}
@Override
public Imports call() {
Arrays.stream(this.generatedClassMetaData.configProperties.getImports())
.forEach(s -> this.blockBuilder.addLineWithEnding("import " + s));
return this;
}
@Override
public boolean accept() {
return this.generatedClassMetaData.configProperties.getImports() != null
&& this.generatedClassMetaData.configProperties.getImports().length > 0;
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import java.util.Arrays;
class CustomStaticImports implements Imports {
private final BlockBuilder blockBuilder;
private final GeneratedClassMetaData generatedClassMetaData;
CustomStaticImports(BlockBuilder blockBuilder,
GeneratedClassMetaData generatedClassMetaData) {
this.blockBuilder = blockBuilder;
this.generatedClassMetaData = generatedClassMetaData;
}
@Override
public Imports call() {
Arrays.stream(this.generatedClassMetaData.configProperties.getStaticImports())
.forEach(s -> this.blockBuilder.addLineWithEnding("import static " + s));
return this;
}
@Override
public boolean accept() {
return this.generatedClassMetaData.configProperties.getStaticImports() != null
&& this.generatedClassMetaData.configProperties
.getStaticImports().length > 0;
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties;
interface DefaultBaseClassProvider {
GeneratedClassMetaData generatedClassMetaData();
BaseClassProvider baseClassProvider();
default String fqnBaseClass() {
ContractVerifierConfigProperties properties = generatedClassMetaData().configProperties;
String includedDirectoryRelativePath = generatedClassMetaData().includedDirectoryRelativePath;
return baseClassProvider().retrieveBaseClass(properties,
includedDirectoryRelativePath);
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import static org.springframework.cloud.contract.verifier.util.NamesUtil.capitalize;
interface DefaultClassMetadata extends ClassMetaData, DefaultBaseClassProvider {
@Override
default ClassMetaData packageDefinition() {
blockBuilder().addLineWithEnding(
"package " + generatedClassMetaData().generatedClassData.classPackage);
return this;
}
GeneratedClassMetaData generatedClassMetaData();
BaseClassProvider baseClassProvider();
BlockBuilder blockBuilder();
@Override
default ClassMetaData className() {
String className = capitalize(
generatedClassMetaData().generatedClassData.className);
blockBuilder().addAtTheEnd(className);
return this;
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import org.springframework.util.StringUtils;
class DefaultImports implements Imports, DefaultBaseClassProvider {
private final BlockBuilder blockBuilder;
private final GeneratedClassMetaData generatedClassMetaData;
private final BaseClassProvider baseClassProvider;
DefaultImports(BlockBuilder blockBuilder,
GeneratedClassMetaData generatedClassMetaData) {
this.blockBuilder = blockBuilder;
this.generatedClassMetaData = generatedClassMetaData;
this.baseClassProvider = new BaseClassProvider();
}
@Override
public Imports call() {
String fqnBaseClass = fqnBaseClass();
if (StringUtils.hasText(fqnBaseClass)) {
this.blockBuilder.addLineWithEnding("import " + fqnBaseClass);
}
return this;
}
@Override
public boolean accept() {
return true;
}
@Override
public GeneratedClassMetaData generatedClassMetaData() {
return this.generatedClassMetaData;
}
@Override
public BaseClassProvider baseClassProvider() {
return this.baseClassProvider;
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import java.util.Arrays;
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
class DefaultJsonStaticImports implements Imports {
private final BlockBuilder blockBuilder;
private final GeneratedClassMetaData generatedClassMetaData;
private static final String[] IMPORTS = {
"com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson" };
DefaultJsonStaticImports(BlockBuilder blockBuilder,
GeneratedClassMetaData generatedClassMetaData) {
this.blockBuilder = blockBuilder;
this.generatedClassMetaData = generatedClassMetaData;
}
@Override
public Imports call() {
Arrays.stream(IMPORTS)
.forEach(s -> this.blockBuilder.addLineWithEnding("import static " + s));
return this;
}
@Override
public boolean accept() {
return this.generatedClassMetaData.listOfFiles.stream()
.anyMatch(metadata -> metadata.getConvertedContractWithMetadata().stream()
.anyMatch(SingleContractMetadata::isJson));
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import java.util.Arrays;
class DefaultStaticImports implements Imports {
private final BlockBuilder blockBuilder;
private static final String[] IMPORTS = {
"org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat",
"org.springframework.cloud.contract.verifier.util.ContractVerifierUtil.*" };
DefaultStaticImports(BlockBuilder blockBuilder) {
this.blockBuilder = blockBuilder;
}
@Override
public Imports call() {
Arrays.stream(IMPORTS)
.forEach(s -> this.blockBuilder.addLineWithEnding("import static " + s));
return this;
}
@Override
public boolean accept() {
return true;
}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import org.springframework.cloud.contract.verifier.config.TestMode;
interface ExplicitAcceptor {
default boolean acceptType(GeneratedClassMetaData generatedClassMetaData) {
return generatedClassMetaData.configProperties.getTestMode() == TestMode.EXPLICIT;
}
}

View File

@@ -1,52 +0,0 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder
import groovy.transform.PackageScope
import groovy.transform.TypeChecked
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
/**
* A {@link JUnitMethodBodyBuilder} implementation that uses Rest Assured in explicit mode
*
* @author Marcin Grzejszczak
*
* @since 1.0.3
*/
@TypeChecked
@PackageScope
class ExplicitJUnitMethodBodyBuilder extends RestAssuredJUnitMethodBodyBuilder {
ExplicitJUnitMethodBodyBuilder(Contract stubDefinition,
ContractVerifierConfigProperties configProperties,
GeneratedClassDataForMethod classDataForMethod) {
super(stubDefinition, configProperties, classDataForMethod)
}
@Override
protected String returnedResponseType() {
return "Response"
}
@Override
protected String returnedRequestType() {
return "RequestSpecification"
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
class ExplicitRequestGiven implements Given, ExplicitAcceptor {
private final BlockBuilder blockBuilder;
private final GeneratedClassMetaData generatedClassMetaData;
ExplicitRequestGiven(BlockBuilder blockBuilder, GeneratedClassMetaData metaData) {
this.blockBuilder = blockBuilder;
this.generatedClassMetaData = metaData;
}
@Override
public MethodVisitor<Given> apply(SingleContractMetadata metadata) {
this.blockBuilder.addIndented("RequestSpecification request = given()");
return this;
}
@Override
public boolean accept(SingleContractMetadata metadata) {
return acceptType(this.generatedClassMetaData);
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
class ExplicitResponseWhen implements When, ExplicitAcceptor {
private final BlockBuilder blockBuilder;
private final GeneratedClassMetaData generatedClassMetaData;
ExplicitResponseWhen(BlockBuilder blockBuilder, GeneratedClassMetaData metaData) {
this.blockBuilder = blockBuilder;
this.generatedClassMetaData = metaData;
}
@Override
public MethodVisitor<When> apply(SingleContractMetadata metadata) {
this.blockBuilder.addIndented("Response response = given().spec(request)");
return this;
}
@Override
public boolean accept(SingleContractMetadata metadata) {
return acceptType(this.generatedClassMetaData);
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import java.util.Arrays;
import org.springframework.cloud.contract.verifier.config.TestMode;
class ExplicitRestAssuredImports implements Imports, RestAssuredVerifier {
private final BlockBuilder blockBuilder;
private final GeneratedClassMetaData generatedClassMetaData;
private static final String[] REST_ASSURED_2_IMPORTS = {
"com.jayway.restassured.specification.RequestSpecification",
"com.jayway.restassured.response.Response" };
private static final String[] REST_ASSURED_3_IMPORTS = {
"io.restassured.specification.RequestSpecification",
"io.restassured.response.Response" };
ExplicitRestAssuredImports(BlockBuilder blockBuilder,
GeneratedClassMetaData generatedClassMetaData) {
this.blockBuilder = blockBuilder;
this.generatedClassMetaData = generatedClassMetaData;
}
@Override
public Imports call() {
Arrays.stream(
isRestAssured2Present() ? REST_ASSURED_2_IMPORTS : REST_ASSURED_3_IMPORTS)
.forEach(s -> this.blockBuilder.addLineWithEnding("import " + s));
return this;
}
@Override
public boolean accept() {
return this.generatedClassMetaData.configProperties
.getTestMode() == TestMode.EXPLICIT
&& this.generatedClassMetaData.isAnyHttp();
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import java.util.Arrays;
import org.springframework.cloud.contract.verifier.config.TestMode;
class ExplicitRestAssuredStaticImports implements Imports, RestAssuredVerifier {
private final BlockBuilder blockBuilder;
private final GeneratedClassMetaData generatedClassMetaData;
private static final String[] REST_ASSURED_2_IMPORTS = {
"com.jayway.restassured.RestAssured.*" };
private static final String[] REST_ASSURED_3_IMPORTS = {
"io.restassured.RestAssured.*" };
ExplicitRestAssuredStaticImports(BlockBuilder blockBuilder,
GeneratedClassMetaData generatedClassMetaData) {
this.blockBuilder = blockBuilder;
this.generatedClassMetaData = generatedClassMetaData;
}
@Override
public Imports call() {
Arrays.stream(
isRestAssured2Present() ? REST_ASSURED_2_IMPORTS : REST_ASSURED_3_IMPORTS)
.forEach(s -> this.blockBuilder.addLineWithEnding("import static " + s));
return this;
}
@Override
public boolean accept() {
return this.generatedClassMetaData.configProperties
.getTestMode() == TestMode.EXPLICIT
&& this.generatedClassMetaData.isAnyHttp();
}
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
interface Field extends Visitor<Field> {
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
class FieldBuilder {
private final ClassBodyBuilder parentBuilder;
private final BlockBuilder builder;
private final GeneratedClassMetaData metaData;
FieldBuilder(ClassBodyBuilder parentBuilder) {
this.parentBuilder = parentBuilder;
this.builder = parentBuilder.blockBuilder;
this.metaData = parentBuilder.generatedClassMetaData;
}
FieldBuilder messaging() {
this.parentBuilder.field(new MessagingFields(this.builder, this.metaData));
return this;
}
ClassBodyBuilder build() {
return this.parentBuilder;
}
}

View File

@@ -1,47 +0,0 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License")
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder
/**
* @author Marcin Grzejszczak
* @since
*/
class GeneratedClassDataForMethod {
final SingleTestGenerator.GeneratedClassData generatedClassData
final String methodName
GeneratedClassDataForMethod(SingleTestGenerator.GeneratedClassData generatedClassData,
String methodName) {
this.generatedClassData = generatedClassData
this.methodName = methodName
}
private SingleTestGenerator.GeneratedClassData assertClassData() {
if (this.generatedClassData == null) {
throw new IllegalStateException("No metadata was found for the generated test class")
}
return this.generatedClassData
}
String className() {
return assertClassData().className
}
java.nio.file.Path testClassPath() {
return assertClassData().testClassPath
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import java.nio.file.Path;
/**
* POJO that wraps data for a given generated method.
*
* @author Marcin Grzejszczak
* @since 2.1.0
*/
public class GeneratedClassDataForMethod {
private final SingleTestGenerator.GeneratedClassData generatedClassData;
private final String methodName;
public GeneratedClassDataForMethod(
SingleTestGenerator.GeneratedClassData generatedClassData,
String methodName) {
this.generatedClassData = generatedClassData;
this.methodName = methodName;
}
private SingleTestGenerator.GeneratedClassData assertClassData() {
if (this.generatedClassData == null) {
throw new IllegalStateException(
"No metadata was found for the generated test class");
}
return this.generatedClassData;
}
public String className() {
return assertClassData().className;
}
public Path testClassPath() {
return assertClassData().testClassPath;
}
public final SingleTestGenerator.GeneratedClassData getGeneratedClassData() {
return generatedClassData;
}
public final String getMethodName() {
return methodName;
}
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import java.util.Collection;
import java.util.stream.Collectors;
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties;
import org.springframework.cloud.contract.verifier.file.ContractMetadata;
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
/**
* All meta data required to generate a test class.
*
* @author Olga Maciaszek-Sharma
* @author Marcin Grzejszczak
* @since 2.2.0
*/
class GeneratedClassMetaData {
final ContractVerifierConfigProperties configProperties;
final Collection<ContractMetadata> listOfFiles;
final String includedDirectoryRelativePath;
final SingleTestGenerator.GeneratedClassData generatedClassData;
GeneratedClassMetaData(ContractVerifierConfigProperties configProperties,
Collection<ContractMetadata> listOfFiles,
String includedDirectoryRelativePath,
SingleTestGenerator.GeneratedClassData generatedClassData) {
this.configProperties = configProperties;
this.listOfFiles = listOfFiles;
this.includedDirectoryRelativePath = includedDirectoryRelativePath;
this.generatedClassData = generatedClassData;
}
Collection<SingleContractMetadata> toSingleContractMetadata() {
return this.listOfFiles.stream()
.flatMap(metadata -> metadata.getConvertedContractWithMetadata().stream())
.collect(Collectors.toList());
}
boolean isAnyJson() {
return toSingleContractMetadata().stream()
.anyMatch(SingleContractMetadata::isJson);
}
boolean isAnyIgnored() {
return toSingleContractMetadata().stream()
.anyMatch(SingleContractMetadata::isIgnored);
}
boolean isAnyXml() {
return toSingleContractMetadata().stream()
.anyMatch(SingleContractMetadata::isXml);
}
boolean isAnyHttp() {
return toSingleContractMetadata().stream()
.anyMatch(SingleContractMetadata::isHttp);
}
boolean isAnyMessaging() {
return toSingleContractMetadata().stream()
.anyMatch(SingleContractMetadata::isMessaging);
}
}

View File

@@ -14,28 +14,25 @@
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder
package org.springframework.cloud.contract.verifier.builder;
/**
* Used to return the {@link Class} against which the type of the element should be verified
* using <code>instanceof</code> in generated response assertions.
* Contents of the generated test.
*
* @author Marcin Grzejszczak
* @author Olga Maciaszek-Sharma
* @since 2.1.0
* @author Marcin Grzejszczak
* @since 2.2.0
*/
trait ClassVerifier {
public class GeneratedTestClass {
Class classToCheck(Object elementFromBody) {
switch (elementFromBody.getClass()) {
case List:
return List
case Set:
return Set
case Map:
return Map
default:
return elementFromBody.class
}
final BlockBuilder blockBuilder;
GeneratedTestClass(BlockBuilder blockBuilder) {
this.blockBuilder = blockBuilder;
}
public String asClassString() {
return this.blockBuilder.toString();
}
}

View File

@@ -0,0 +1,182 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
/**
* A generated test class consists of the class meta data (e.g. packages, imports) fields
* and methods. The latter are generated via the {@link ClassBodyBuilder}.
*
* @author Olga Maciaszek-Sharma
* @author Marcin Grzejszczak
* @since 2.2.0
*/
class GeneratedTestClassBuilder {
private List<ClassMetaData> metaData = new LinkedList<>();
private List<Imports> imports = new LinkedList<>();
private List<Imports> staticImports = new LinkedList<>();
private List<ClassAnnotation> annotations = new LinkedList<>();
private ClassBodyBuilder classBodyBuilder;
final BlockBuilder blockBuilder;
final GeneratedClassMetaData generatedClassMetaData;
private GeneratedTestClassBuilder(BlockBuilder blockBuilder,
GeneratedClassMetaData generatedClassMetaData) {
this.blockBuilder = blockBuilder;
this.generatedClassMetaData = generatedClassMetaData;
}
static GeneratedTestClassBuilder builder(BlockBuilder blockBuilder,
GeneratedClassMetaData generatedClassMetaData) {
return new GeneratedTestClassBuilder(blockBuilder, generatedClassMetaData);
}
GeneratedTestClassBuilder metaData(ClassMetaData metaData) {
this.metaData.add(metaData);
return this;
}
MetaDataBuilder metaData() {
return new MetaDataBuilder(this);
}
GeneratedTestClassBuilder metaData(ClassMetaData... metaData) {
return metaData(Arrays.asList(metaData));
}
GeneratedTestClassBuilder metaData(List<ClassMetaData> metaData) {
this.metaData.addAll(metaData);
return this;
}
ImportsBuilder imports() {
return new ImportsBuilder(this);
}
GeneratedTestClassBuilder imports(Imports imports) {
this.imports.add(imports);
return this;
}
GeneratedTestClassBuilder imports(Imports... imports) {
return imports(Arrays.asList(imports));
}
GeneratedTestClassBuilder imports(List<Imports> imports) {
this.imports.addAll(imports);
return this;
}
GeneratedTestClassBuilder staticImports(Imports imports) {
this.staticImports.add(imports);
return this;
}
GeneratedTestClassBuilder staticImports(Imports... imports) {
return staticImports(Arrays.asList(imports));
}
GeneratedTestClassBuilder staticImports(List<Imports> imports) {
this.staticImports.addAll(imports);
return this;
}
ClassAnnotationsBuilder classAnnotations() {
return new ClassAnnotationsBuilder(this);
}
GeneratedTestClassBuilder classAnnotations(ClassAnnotation... annotations) {
List<ClassAnnotation> classAnnotations = Arrays.asList(annotations);
this.annotations.addAll(classAnnotations);
return this;
}
GeneratedTestClassBuilder classBodyBuilder(ClassBodyBuilder classBodyBuilder) {
this.classBodyBuilder = classBodyBuilder;
return this;
}
/**
* From a matching {@link ClassMetaData} given the present input data, builds a
* generated test class.
* @return generated test class
*/
GeneratedTestClass build() {
// picks a matching class meta data
ClassMetaData classMetaData = this.metaData.stream().filter(Acceptor::accept)
.findFirst().orElseThrow(() -> new IllegalStateException(
"There is no matching class meta data"));
classMetaData.setupLineEnding().setupLabelPrefix()
// package com.example
.packageDefinition();
// \n
this.blockBuilder.addEmptyLine();
// import ... \n
visit(this.imports);
// import static ... \n
visit(this.staticImports);
// @Test ... \n
visitWithNoEnding(this.annotations);
// @formatter:off
// public
this.blockBuilder.append(classMetaData::modifier)
// class
.appendWithSpace("class")
// Foo
.appendWithSpace(classMetaData::className)
// Spec
.append(classMetaData::suffix)
// extends Parent
.appendWithSpace(classMetaData::parentClass);
// public class FooSpec extends Parent
// @formatter:on
this.classBodyBuilder.build();
return new GeneratedTestClass(this.blockBuilder);
}
void visit(List<? extends Visitor> list) {
visit(list, true);
}
void visitWithNoEnding(List<? extends Visitor> list) {
visit(list, false);
}
private void visit(List<? extends Visitor> list, boolean addEnding) {
List<Visitor> elements = list.stream().filter(Acceptor::accept)
.collect(Collectors.toList());
elements.forEach(OurCallable::call);
if (addEnding) {
this.blockBuilder.addEndingIfNotPresent();
}
if (!elements.isEmpty()) {
this.blockBuilder.addEmptyLine();
}
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import org.springframework.cloud.contract.spec.internal.FromFileProperty;
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
class GenericBinaryBodyThen implements Then {
private final BlockBuilder blockBuilder;
private final BodyAssertionLineCreator bodyAssertionLineCreator;
private final BodyParser bodyParser;
private final ComparisonBuilder comparisonBuilder;
GenericBinaryBodyThen(BlockBuilder blockBuilder, GeneratedClassMetaData metaData,
BodyParser bodyParser, ComparisonBuilder comparisonBuilder) {
this.blockBuilder = blockBuilder;
this.comparisonBuilder = comparisonBuilder;
this.bodyAssertionLineCreator = new BodyAssertionLineCreator(blockBuilder,
metaData, bodyParser.byteArrayString(), this.comparisonBuilder);
this.bodyParser = bodyParser;
}
@Override
public MethodVisitor<Then> apply(SingleContractMetadata metadata) {
Object responseBody = this.bodyParser.responseBody(metadata).getServerValue();
byteResponseBodyCheck(metadata, (FromFileProperty) responseBody);
return this;
}
private void byteResponseBodyCheck(SingleContractMetadata metadata,
FromFileProperty convertedResponseBody) {
this.bodyAssertionLineCreator.appendBodyAssertionLine(metadata, "",
convertedResponseBody);
this.blockBuilder.addEndingIfNotPresent();
}
@Override
public boolean accept(SingleContractMetadata metadata) {
Object responseBody = this.bodyParser.responseBody(metadata).getServerValue();
if (!(responseBody instanceof FromFileProperty)) {
return false;
}
return ((FromFileProperty) responseBody).isByte();
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import org.springframework.cloud.contract.spec.internal.Request;
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
import org.springframework.cloud.contract.verifier.template.HandlebarsTemplateProcessor;
import org.springframework.cloud.contract.verifier.template.TemplateProcessor;
class GenericHttpBodyThen implements Then, BodyMethodVisitor {
private final BlockBuilder blockBuilder;
private final BodyParser bodyParser;
private final TemplateProcessor templateProcessor;
private final ComparisonBuilder comparisonBuilder;
private final List<Then> thens = new LinkedList<>();
GenericHttpBodyThen(BlockBuilder blockBuilder, GeneratedClassMetaData metaData,
BodyParser bodyParser, ComparisonBuilder comparisonBuilder) {
this.blockBuilder = blockBuilder;
this.bodyParser = bodyParser;
this.comparisonBuilder = comparisonBuilder;
this.templateProcessor = new HandlebarsTemplateProcessor();
this.thens.addAll(Arrays.asList(
new GenericBinaryBodyThen(blockBuilder, metaData, this.bodyParser,
comparisonBuilder),
new GenericTextBodyThen(blockBuilder, metaData, this.bodyParser,
this.comparisonBuilder),
new GenericJsonBodyThen(blockBuilder, metaData, this.bodyParser,
this.comparisonBuilder),
new GenericXmlBodyThen(blockBuilder, this.bodyParser)));
}
@Override
public MethodVisitor<Then> apply(SingleContractMetadata metadata) {
endBodyBlock(this.blockBuilder);
this.blockBuilder.addEmptyLine();
startBodyBlock(this.blockBuilder, "and:");
Request request = metadata.getContract().getRequest();
this.thens.stream().filter(then -> then.accept(metadata))
.forEach(then -> then.apply(metadata));
String newBody = this.templateProcessor.transform(request,
this.blockBuilder.toString());
this.blockBuilder.updateContents(newBody);
return this;
}
@Override
public boolean accept(SingleContractMetadata metadata) {
return metadata.getContract().getResponse().getBody() != null;
}
}

View File

@@ -0,0 +1,220 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import org.apache.commons.text.StringEscapeUtils;
import org.springframework.cloud.contract.spec.ContractTemplate;
import org.springframework.cloud.contract.spec.internal.BodyMatchers;
import org.springframework.cloud.contract.spec.internal.ExecutionProperty;
import org.springframework.cloud.contract.verifier.config.TestFramework;
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
import org.springframework.cloud.contract.verifier.template.HandlebarsTemplateProcessor;
import org.springframework.cloud.contract.verifier.template.TemplateProcessor;
import org.springframework.cloud.contract.verifier.util.ContentType;
import org.springframework.cloud.contract.verifier.util.MapConverter;
import org.springframework.util.StringUtils;
import static org.springframework.cloud.contract.verifier.util.ContentType.DEFINED;
import static org.springframework.cloud.contract.verifier.util.ContentType.FORM;
import static org.springframework.cloud.contract.verifier.util.ContentType.JSON;
import static org.springframework.cloud.contract.verifier.util.ContentType.TEXT;
class GenericJsonBodyThen implements Then {
private final BlockBuilder blockBuilder;
private final GeneratedClassMetaData generatedClassMetaData;
private final BodyParser bodyParser;
private final BodyAssertionLineCreator bodyAssertionLineCreator;
private final TemplateProcessor templateProcessor;
private final ContractTemplate contractTemplate;
private final ComparisonBuilder comparisonBuilder;
GenericJsonBodyThen(BlockBuilder blockBuilder, GeneratedClassMetaData metaData,
BodyParser bodyParser, ComparisonBuilder comparisonBuilder) {
this.blockBuilder = blockBuilder;
this.bodyParser = bodyParser;
this.comparisonBuilder = comparisonBuilder;
this.bodyAssertionLineCreator = new BodyAssertionLineCreator(blockBuilder,
metaData, this.bodyParser.byteArrayString(), this.comparisonBuilder);
this.generatedClassMetaData = metaData;
this.templateProcessor = new HandlebarsTemplateProcessor();
this.contractTemplate = new HandlebarsTemplateProcessor();
}
@Override
public MethodVisitor<Then> apply(SingleContractMetadata metadata) {
BodyMatchers bodyMatchers = this.bodyParser.responseBodyMatchers(metadata);
Object convertedResponseBody = this.bodyParser.convertResponseBody(metadata);
ContentType contentType = metadata.getOutputTestContentType();
if (TEXT != contentType && FORM != contentType && DEFINED != contentType) {
boolean dontParseStrings = contentType == JSON
&& convertedResponseBody instanceof Map;
Function parsingClosure = dontParseStrings ? Function.identity()
: MapConverter.JSON_PARSING_FUNCTION;
convertedResponseBody = MapConverter.getTestSideValues(convertedResponseBody,
parsingClosure);
}
else {
convertedResponseBody = StringEscapeUtils
.escapeJava(convertedResponseBody.toString());
}
addJsonBodyVerification(metadata, convertedResponseBody, bodyMatchers);
return this;
}
private void addJsonBodyVerification(SingleContractMetadata contractMetadata,
Object responseBody, BodyMatchers bodyMatchers) {
JsonBodyVerificationBuilder jsonBodyVerificationBuilder = new JsonBodyVerificationBuilder(
this.generatedClassMetaData.configProperties, this.templateProcessor,
this.contractTemplate, contractMetadata.getContract(),
Optional.of(this.blockBuilder.getLineEnding()),
bodyParser::postProcessJsonPath);
// TODO: Refactor spock from should comment out bdd blocks
Object convertedResponseBody = jsonBodyVerificationBuilder
.addJsonResponseBodyCheck(this.blockBuilder, responseBody, bodyMatchers,
this.bodyParser.responseAsString(),
this.generatedClassMetaData.configProperties
.getTestFramework() != TestFramework.SPOCK);
if (!(convertedResponseBody instanceof Map
|| convertedResponseBody instanceof List)) {
simpleTextResponseBodyCheck(contractMetadata, convertedResponseBody);
}
processBodyElement("", "", convertedResponseBody);
}
private void processBodyElement(String oldProp, String property, Object value) {
String propDiff = subtract(property, oldProp);
String prop = wrappedWithBracketsForDottedProp(propDiff);
String mergedProp = StringUtils.hasText(property) ? oldProp + "." + prop : "";
if (value instanceof ExecutionProperty) {
processBodyElement(mergedProp, (ExecutionProperty) value);
}
else if (value instanceof Map.Entry) {
processBodyElement(mergedProp, (Map.Entry) value);
}
else if (value instanceof Map) {
processBodyElement(mergedProp, (Map) value);
}
else if (value instanceof List) {
processBodyElement(mergedProp, (List) value);
}
}
private void processBodyElement(String property, ExecutionProperty exec) {
this.blockBuilder.addLineWithEnding(exec.insertValue(this.bodyParser
.postProcessJsonPath("parsedJson.read(\"$" + property + "\")")));
}
private void processBodyElement(String property, Map.Entry entry) {
processBodyElement(property, getMapKeyReferenceString(property, entry),
entry.getValue());
}
private void processBodyElement(String property, Map map) {
map.entrySet().forEach(o -> processBodyElement(property, (Map.Entry) o));
}
private void processBodyElement(String property, List list) {
Iterator iterator = list.iterator();
int index = -1;
while (iterator.hasNext()) {
Object listElement = iterator.next();
index = index + 1;
String prop = getPropertyInListString(property, index);
processBodyElement(property, prop, listElement);
}
}
private String getPropertyInListString(String property, Integer listIndex) {
return property + "[" + listIndex + "]";
}
private String getMapKeyReferenceString(String property, Map.Entry entry) {
return provideProperJsonPathNotation(property) + "." + entry.getKey();
}
private String provideProperJsonPathNotation(String property) {
return property.replaceAll("(get\\(\\\\\")(.*)(\\\\\"\\))", "$2");
}
private String wrappedWithBracketsForDottedProp(String key) {
String remindingKey = trailingKey(key);
if (remindingKey.contains(".")) {
return "['" + remindingKey + "']";
}
return remindingKey;
}
private String trailingKey(String key) {
if (key.startsWith(".")) {
return key.substring(1);
}
return key;
}
private String subtract(String self, String text) {
int index = self.indexOf(text);
if (index == -1) {
return self;
}
int end = index + text.length();
if (self.length() > end) {
return self.substring(0, index) + self.substring(end);
}
return self.substring(0, index);
}
private void simpleTextResponseBodyCheck(SingleContractMetadata metadata,
Object convertedResponseBody) {
this.blockBuilder.addLineWithEnding(
getSimpleResponseBodyString(this.bodyParser.responseAsString()));
this.bodyAssertionLineCreator.appendBodyAssertionLine(metadata, "",
convertedResponseBody);
this.blockBuilder.addEndingIfNotPresent();
}
private String getSimpleResponseBodyString(String responseString) {
return "String responseBody = " + responseString
+ this.blockBuilder.getLineEnding();
}
@Override
public boolean accept(SingleContractMetadata metadata) {
ContentType outputTestContentType = metadata.getOutputTestContentType();
return JSON == outputTestContentType
|| mostLikelyJson(outputTestContentType, metadata);
}
private boolean mostLikelyJson(ContentType outputTestContentType,
SingleContractMetadata metadata) {
return DEFINED == outputTestContentType && metadata.evaluatesToJson();
}
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import org.springframework.cloud.contract.spec.internal.FromFileProperty;
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
import org.springframework.cloud.contract.verifier.util.ContentType;
import static org.springframework.cloud.contract.verifier.util.ContentType.JSON;
import static org.springframework.cloud.contract.verifier.util.ContentType.XML;
class GenericTextBodyThen implements Then {
private final BlockBuilder blockBuilder;
private final BodyAssertionLineCreator bodyAssertionLineCreator;
private final BodyParser bodyParser;
private final ComparisonBuilder comparisonBuilder;
GenericTextBodyThen(BlockBuilder blockBuilder, GeneratedClassMetaData metaData,
BodyParser bodyParser, ComparisonBuilder comparisonBuilder) {
this.blockBuilder = blockBuilder;
this.bodyParser = bodyParser;
this.comparisonBuilder = comparisonBuilder;
this.bodyAssertionLineCreator = new BodyAssertionLineCreator(blockBuilder,
metaData, this.bodyParser.byteArrayString(), this.comparisonBuilder);
}
@Override
public MethodVisitor<Then> apply(SingleContractMetadata metadata) {
Object convertedResponseBody = this.bodyParser.convertResponseBody(metadata);
if (convertedResponseBody instanceof String) {
convertedResponseBody = this.bodyParser
.escapeForSimpleTextAssertion(convertedResponseBody.toString());
}
simpleTextResponseBodyCheck(metadata, convertedResponseBody);
return this;
}
private void simpleTextResponseBodyCheck(SingleContractMetadata metadata,
Object convertedResponseBody) {
this.blockBuilder.addLineWithEnding(
getSimpleResponseBodyString(this.bodyParser.responseAsString()));
this.bodyAssertionLineCreator.appendBodyAssertionLine(metadata, "",
convertedResponseBody);
this.blockBuilder.addEndingIfNotPresent();
}
private String getSimpleResponseBodyString(String responseString) {
return "String responseBody = " + responseString
+ this.blockBuilder.getLineEnding();
}
@Override
public boolean accept(SingleContractMetadata metadata) {
ContentType outputTestContentType = metadata.getOutputTestContentType();
return outputTestContentType != JSON && outputTestContentType != XML
&& this.bodyParser.responseBody(metadata) != null
&& !(this.bodyParser.responseBody(metadata)
.getServerValue() instanceof FromFileProperty);
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import java.util.Optional;
import org.springframework.cloud.contract.spec.internal.BodyMatchers;
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
import org.springframework.cloud.contract.verifier.util.ContentType;
import static org.springframework.cloud.contract.verifier.util.ContentType.XML;
class GenericXmlBodyThen implements Then {
private final BlockBuilder blockBuilder;
private final BodyParser bodyParser;
GenericXmlBodyThen(BlockBuilder blockBuilder, BodyParser bodyParser) {
this.blockBuilder = blockBuilder;
this.bodyParser = bodyParser;
}
@Override
public MethodVisitor<Then> apply(SingleContractMetadata metadata) {
BodyMatchers bodyMatchers = this.bodyParser.responseBodyMatchers(metadata);
Object convertedResponseBody = this.bodyParser.convertResponseBody(metadata);
XmlBodyVerificationBuilder xmlBodyVerificationBuilder = new XmlBodyVerificationBuilder(
metadata.getContract(), Optional.of(this.blockBuilder.getLineEnding()));
xmlBodyVerificationBuilder.addXmlResponseBodyCheck(this.blockBuilder,
convertedResponseBody, bodyMatchers, this.bodyParser.responseAsString(),
true);
return this;
}
@Override
public boolean accept(SingleContractMetadata metadata) {
ContentType outputTestContentType = metadata.getOutputTestContentType();
return XML == outputTestContentType;
}
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
interface Given extends MethodVisitor<Given> {
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import org.apache.commons.text.StringEscapeUtils;
import org.springframework.cloud.contract.verifier.template.TemplateProcessor;
interface GroovyBodyParser extends BodyParser {
@Override
default String convertUnicodeEscapesIfRequired(String json) {
return StringEscapeUtils.unescapeEcmaScript(json);
}
@Override
default String postProcessJsonPath(String jsonPath) {
if (templateProcessor().containsTemplateEntry(jsonPath)) {
return jsonPath;
}
return jsonPath.replace("$", "\\$");
}
TemplateProcessor templateProcessor();
@Override
default String escape(String text) {
return text.replaceAll("\\n", "\\\\n");
}
@Override
default String escapeForSimpleTextAssertion(String text) {
return escape(text);
}
@Override
default String quotedShortText(Object text) {
String string = text.toString();
if (text instanceof Number) {
return string;
}
else if (string.contains("'") || string.contains("\"")) {
return quotedLongText(text);
}
return "'" + groovyEscapedString(text.toString()) + "'";
}
@Override
default String quotedEscapedShortText(Object text) {
String string = text.toString();
if (text instanceof Number) {
return string;
}
else if (string.contains("'") || string.contains("\"")) {
return quotedEscapedLongText(text);
}
return "'" + text.toString() + "'";
}
@Override
default String quotedEscapedLongText(Object text) {
return "'''" + text.toString() + "'''";
}
@Override
default String quotedLongText(Object text) {
return "'''" + groovyEscapedString(text) + "'''";
}
default String groovyEscapedString(Object text) {
return escape(text.toString()).replaceAll("\\\\\"", "\"");
}
}

View File

@@ -0,0 +1,108 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties;
import org.springframework.cloud.contract.verifier.config.TestFramework;
import org.springframework.util.StringUtils;
class GroovyClassMetaData implements ClassMetaData, DefaultClassMetadata {
private final BlockBuilder blockBuilder;
private final BaseClassProvider baseClassProvider = new BaseClassProvider();
private final GeneratedClassMetaData generatedClassMetaData;
GroovyClassMetaData(BlockBuilder blockBuilder,
GeneratedClassMetaData generatedClassMetaData) {
this.blockBuilder = blockBuilder;
this.generatedClassMetaData = generatedClassMetaData;
}
@Override
public ClassMetaData setupLineEnding() {
return this;
}
@Override
public ClassMetaData setupLabelPrefix() {
return this;
}
@Override
public ClassMetaData suffix() {
String suffix = StringUtils.hasText(
this.generatedClassMetaData.configProperties.getNameSuffixForTests())
? this.generatedClassMetaData.configProperties
.getNameSuffixForTests()
: "Spec";
if (!this.blockBuilder.endsWith(suffix)) {
this.blockBuilder.addAtTheEnd(suffix);
}
return this;
}
@Override
public ClassMetaData modifier() {
return this;
}
@Override
public ClassMetaData packageDefinition() {
this.blockBuilder.addLineWithEnding(
"package " + this.generatedClassMetaData.generatedClassData.classPackage);
return this;
}
@Override
public ClassMetaData parentClass() {
ContractVerifierConfigProperties properties = generatedClassMetaData().configProperties;
String includedDirectoryRelativePath = generatedClassMetaData().includedDirectoryRelativePath;
String baseClass = baseClassProvider().retrieveBaseClass(properties,
includedDirectoryRelativePath);
baseClass = StringUtils.hasText(baseClass) ? baseClass : "Specification";
int lastIndexOf = baseClass.lastIndexOf(".");
if (lastIndexOf > 0) {
baseClass = baseClass.substring(lastIndexOf + 1);
}
blockBuilder().append("extends ").append(baseClass).append(" ");
return this;
}
@Override
public GeneratedClassMetaData generatedClassMetaData() {
return this.generatedClassMetaData;
}
@Override
public BaseClassProvider baseClassProvider() {
return this.baseClassProvider;
}
@Override
public BlockBuilder blockBuilder() {
return this.blockBuilder;
}
@Override
public boolean accept() {
return this.generatedClassMetaData.configProperties
.getTestFramework() == TestFramework.SPOCK;
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import java.util.regex.Pattern;
import org.apache.commons.text.StringEscapeUtils;
interface GroovyComparisonBuilder extends ComparisonBuilder {
ComparisonBuilder SPOCK_HTTP_INSTANCE = (GroovyComparisonBuilder) () -> SpockRestAssuredBodyParser.INSTANCE;
ComparisonBuilder JAXRS_HTTP_INSTANCE = (GroovyComparisonBuilder) () -> JaxRsBodyParser.INSTANCE;
ComparisonBuilder SPOCK_MESSAGING_INSTANCE = (GroovyComparisonBuilder) () -> SpockMessagingBodyParser.INSTANCE;
@Override
default String assertThat(String object) {
return object;
}
@Override
default String isEqualToUnquoted(String unquoted) {
return " == " + unquoted;
}
@Override
default String isEqualTo(Number number) {
return " == " + number.toString();
}
@Override
default String matches(String pattern) {
return matchesEscaped(bodyParser().quotedShortText(pattern));
}
@Override
default String matches(Pattern pattern) {
String escapedPattern = StringEscapeUtils.escapeJava(pattern.pattern());
return matchesEscaped(escapedPattern);
}
@Override
default String matchesEscaped(String pattern) {
return " ==~ java.util.regex.Pattern.compile("
+ bodyParser().quotedEscapedShortText(pattern) + ")";
}
@Override
default String isNotNull() {
return " != null";
}
}

View File

@@ -1,143 +0,0 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder
import java.util.regex.Pattern
import groovy.transform.PackageScope
import groovy.transform.TypeChecked
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.spec.internal.Cookie
import org.springframework.cloud.contract.spec.internal.ExecutionProperty
import org.springframework.cloud.contract.spec.internal.Header
import org.springframework.cloud.contract.spec.internal.NotToEscapePattern
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
import org.springframework.cloud.contract.verifier.util.MapConverter
/**
* A {@link SpockMethodRequestProcessingBodyBuilder} implementation that uses MockMvc to send requests.
*
* @since 1.0.0
*/
@PackageScope
@TypeChecked
class HttpSpockMethodRequestProcessingBodyBuilder extends SpockMethodRequestProcessingBodyBuilder {
HttpSpockMethodRequestProcessingBodyBuilder(Contract stubDefinition,
ContractVerifierConfigProperties configProperties,
GeneratedClassDataForMethod classDataForMethod) {
super(stubDefinition, configProperties, classDataForMethod)
}
@Override
protected void validateResponseCodeBlock(BlockBuilder bb) {
bb.addLine("response.statusCode == $response.status.serverValue")
}
@Override
protected void validateResponseHeadersBlock(BlockBuilder bb) {
response.headers?.executeForEachHeader { Header header ->
processHeaderElement(bb, header.name, header.serverValue instanceof NotToEscapePattern ?
header.serverValue :
MapConverter.getTestSideValues(header.serverValue))
}
}
@Override
protected void validateResponseCookiesBlock(BlockBuilder bb) {
response.cookies?.executeForEachCookie { Cookie cookie ->
processCookieElement(bb, cookie.key, cookie.serverValue instanceof NotToEscapePattern ?
cookie.serverValue :
MapConverter.getTestSideValues(cookie.serverValue))
}
}
@Override
protected String getResponseAsString() {
return 'response.body.asString()'
}
@Override
protected void processHeaderElement(BlockBuilder blockBuilder, String property, Object value) {
if (value instanceof NotToEscapePattern) {
blockBuilder.addLine("response.header('$property') "
+
"${patternComparison(((NotToEscapePattern) value).serverValue.pattern().replace("\\", "\\\\"))}")
}
else {
// fallback
processHeaderElement(blockBuilder, property, value.toString())
}
}
@Override
protected void processCookieElement(BlockBuilder blockBuilder, String key, Object value) {
if (value instanceof NotToEscapePattern) {
blockBuilder.addLine("response.cookie('$key') "
+
"${patternComparison(((NotToEscapePattern) value).serverValue.pattern().replace("\\", "\\\\"))}")
}
else {
processCookieElement(blockBuilder, key, value.toString())
}
}
@Override
protected void processHeaderElement(BlockBuilder blockBuilder, String property, Number number) {
blockBuilder.addLine("response.header('$property') == ${number}")
}
@Override
protected void processHeaderElement(BlockBuilder blockBuilder, String property, ExecutionProperty exec) {
blockBuilder.addLine("${exec.insertValue("response.header(\'$property\')")}")
}
@Override
protected void processHeaderElement(BlockBuilder blockBuilder, String property, String value) {
blockBuilder.
addLine("response.header('$property') ${convertHeaderComparison(value)}")
}
@Override
protected void processHeaderElement(BlockBuilder blockBuilder, String property, Pattern value) {
blockBuilder.
addLine("response.header('$property') ${convertHeaderComparison(value)}")
}
@Override
protected void processCookieElement(BlockBuilder blockBuilder, String key, Pattern pattern) {
blockBuilder.addLine("response.cookie('$key') != null")
blockBuilder.
addLine("response.cookie('$key') ${convertCookieComparison(pattern)}")
}
@Override
protected void processCookieElement(BlockBuilder blockBuilder, String key, String value) {
blockBuilder.addLine("response.cookie('$key') != null")
blockBuilder.addLine("response.cookie('$key') ${convertCookieComparison(value)}")
}
// #273 - should escape $ for Groovy since it will try to make it a GString
@Override
protected String postProcessJsonPathCall(String jsonPath) {
if (templateProcessor.containsTemplateEntry(jsonPath)) {
return jsonPath
}
return jsonPath.replace('$', '\\$')
}
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
interface Imports extends Visitor<Imports> {
}

View File

@@ -0,0 +1,105 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
class ImportsBuilder {
private final GeneratedTestClassBuilder parentBuilder;
private final BlockBuilder builder;
private final GeneratedClassMetaData metaData;
ImportsBuilder(GeneratedTestClassBuilder generatedTestClassBuilder) {
this.parentBuilder = generatedTestClassBuilder;
this.builder = generatedTestClassBuilder.blockBuilder;
this.metaData = generatedTestClassBuilder.generatedClassMetaData;
}
ImportsBuilder defaultImports() {
this.parentBuilder.imports(new DefaultImports(builder, metaData));
this.parentBuilder.staticImports(new DefaultStaticImports(builder));
return this;
}
ImportsBuilder custom() {
this.parentBuilder.imports(new CustomImports(builder, metaData));
this.parentBuilder.staticImports(new CustomStaticImports(builder, metaData));
return this;
}
ImportsBuilder json() {
this.parentBuilder.imports(new JsonImports(builder, metaData),
new JsonPathImports(builder, metaData));
this.parentBuilder.staticImports(new DefaultJsonStaticImports(builder, metaData));
return this;
}
ImportsBuilder xml() {
this.parentBuilder.imports(new XmlImports(builder, metaData));
return this;
}
ImportsBuilder jUnit4() {
this.parentBuilder.imports(new JUnit4Imports(builder, metaData),
new JUnit4IgnoreImports(builder, metaData),
new JUnit4OrderImports(builder, metaData));
return this;
}
ImportsBuilder jUnit5() {
this.parentBuilder.imports(new JUnit5Imports(builder, metaData),
new JUnit5IgnoreImports(builder, metaData),
new JUnit5OrderImports(builder, metaData));
return this;
}
ImportsBuilder spock() {
this.parentBuilder.imports(new SpockImports(builder, metaData),
new SpockIgnoreImports(builder, metaData),
new SpockOrderImports(builder, metaData));
return this;
}
ImportsBuilder messaging() {
this.parentBuilder.imports(new MessagingImports(builder, metaData));
this.parentBuilder.staticImports(new MessagingStaticImports(builder, metaData));
return this;
}
ImportsBuilder restAssured() {
this.parentBuilder.imports(new MockMvcRestAssuredImports(builder, metaData),
new ExplicitRestAssuredImports(builder, metaData),
new WebTestClientRestAssuredImports(builder, metaData));
this.parentBuilder.staticImports(
new MockMvcRestAssuredStaticImports(builder, metaData),
new ExplicitRestAssuredStaticImports(builder, metaData),
new WebTestClientRestAssured3StaticImports(builder, metaData));
return this;
}
ImportsBuilder jaxRs() {
this.parentBuilder.imports(new JaxRsImports(builder, metaData));
this.parentBuilder.staticImports(new JaxRsStaticImports(builder, metaData));
return this;
}
GeneratedTestClassBuilder build() {
return this.parentBuilder;
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import org.springframework.cloud.contract.verifier.config.TestFramework;
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
class JUnit4IgnoreImports implements Imports {
private final BlockBuilder blockBuilder;
private final GeneratedClassMetaData generatedClassMetaData;
JUnit4IgnoreImports(BlockBuilder blockBuilder,
GeneratedClassMetaData generatedClassMetaData) {
this.blockBuilder = blockBuilder;
this.generatedClassMetaData = generatedClassMetaData;
}
@Override
public Imports call() {
this.blockBuilder.addLineWithEnding("import org.junit.Ignore");
return this;
}
@Override
public boolean accept() {
return this.generatedClassMetaData.configProperties
.getTestFramework() == TestFramework.JUNIT
&& this.generatedClassMetaData.listOfFiles.stream()
.anyMatch(metadata -> metadata.isIgnored()
|| metadata.getConvertedContractWithMetadata().stream()
.anyMatch(SingleContractMetadata::isIgnored));
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import java.util.Arrays;
import org.springframework.cloud.contract.verifier.config.TestFramework;
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
class JUnit4IgnoreMethodAnnotation implements MethodAnnotations {
private final BlockBuilder blockBuilder;
private final GeneratedClassMetaData generatedClassMetaData;
private static final String[] ANNOTATIONS = { "@Ignore" };
JUnit4IgnoreMethodAnnotation(BlockBuilder blockBuilder,
GeneratedClassMetaData generatedClassMetaData) {
this.blockBuilder = blockBuilder;
this.generatedClassMetaData = generatedClassMetaData;
}
@Override
public MethodVisitor<MethodAnnotations> apply(
SingleContractMetadata singleContractMetadata) {
Arrays.stream(ANNOTATIONS).forEach(this.blockBuilder::addIndented);
return this;
}
@Override
public boolean accept(SingleContractMetadata singleContractMetadata) {
return this.generatedClassMetaData.configProperties
.getTestFramework() == TestFramework.JUNIT
&& this.generatedClassMetaData.isAnyIgnored();
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import java.util.Arrays;
import org.springframework.cloud.contract.verifier.config.TestFramework;
class JUnit4Imports implements Imports {
private final BlockBuilder blockBuilder;
private final GeneratedClassMetaData generatedClassMetaData;
private static final String[] IMPORTS = { "org.junit.Test", "org.junit.Rule" };
JUnit4Imports(BlockBuilder blockBuilder,
GeneratedClassMetaData generatedClassMetaData) {
this.blockBuilder = blockBuilder;
this.generatedClassMetaData = generatedClassMetaData;
}
@Override
public Imports call() {
Arrays.stream(IMPORTS)
.forEach(s -> this.blockBuilder.addLineWithEnding("import " + s));
return this;
}
@Override
public boolean accept() {
return this.generatedClassMetaData.configProperties
.getTestFramework() == TestFramework.JUNIT;
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import java.util.Arrays;
import org.springframework.cloud.contract.verifier.config.TestFramework;
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
class JUnit4MethodAnnotation implements MethodAnnotations {
private final BlockBuilder blockBuilder;
private final GeneratedClassMetaData generatedClassMetaData;
private static final String[] ANNOTATIONS = { "@Test" };
JUnit4MethodAnnotation(BlockBuilder blockBuilder,
GeneratedClassMetaData generatedClassMetaData) {
this.blockBuilder = blockBuilder;
this.generatedClassMetaData = generatedClassMetaData;
}
@Override
public boolean accept(SingleContractMetadata singleContractMetadata) {
return this.generatedClassMetaData.configProperties
.getTestFramework() == TestFramework.JUNIT;
}
@Override
public MethodVisitor<MethodAnnotations> apply(
SingleContractMetadata singleContractMetadata) {
Arrays.stream(ANNOTATIONS).forEach(this.blockBuilder::addIndented);
return this;
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import java.util.Arrays;
import org.springframework.cloud.contract.verifier.config.TestFramework;
class JUnit4OrderClassAnnotation implements ClassAnnotation {
private final BlockBuilder blockBuilder;
private final GeneratedClassMetaData generatedClassMetaData;
private static final String[] ANNOTATIONS = {
"@FixMethodOrder(MethodSorters.NAME_ASCENDING)" };
JUnit4OrderClassAnnotation(BlockBuilder blockBuilder,
GeneratedClassMetaData generatedClassMetaData) {
this.blockBuilder = blockBuilder;
this.generatedClassMetaData = generatedClassMetaData;
}
@Override
public ClassAnnotation call() {
Arrays.stream(ANNOTATIONS).forEach(this.blockBuilder::addIndented);
return this;
}
@Override
public boolean accept() {
return this.generatedClassMetaData.configProperties
.getTestFramework() == TestFramework.JUNIT
&& this.generatedClassMetaData.listOfFiles.stream()
.anyMatch(meta -> meta.getOrder() != null);
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import java.util.Arrays;
import org.springframework.cloud.contract.verifier.config.TestFramework;
class JUnit4OrderImports implements Imports {
private final BlockBuilder blockBuilder;
private final GeneratedClassMetaData generatedClassMetaData;
private static final String[] IMPORTS = { "org.junit.FixMethodOrder",
"org.junit.runners.MethodSorters" };
JUnit4OrderImports(BlockBuilder blockBuilder,
GeneratedClassMetaData generatedClassMetaData) {
this.blockBuilder = blockBuilder;
this.generatedClassMetaData = generatedClassMetaData;
}
@Override
public Imports call() {
Arrays.stream(IMPORTS)
.forEach(s -> this.blockBuilder.addLineWithEnding("import " + s));
return this;
}
@Override
public boolean accept() {
return this.generatedClassMetaData.configProperties
.getTestFramework() == TestFramework.JUNIT
&& this.generatedClassMetaData.listOfFiles.stream()
.anyMatch(meta -> meta.getOrder() != null);
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import org.springframework.cloud.contract.verifier.config.TestFramework;
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
class JUnit5IgnoreImports implements Imports {
private final BlockBuilder blockBuilder;
private final GeneratedClassMetaData generatedClassMetaData;
JUnit5IgnoreImports(BlockBuilder blockBuilder,
GeneratedClassMetaData generatedClassMetaData) {
this.blockBuilder = blockBuilder;
this.generatedClassMetaData = generatedClassMetaData;
}
@Override
public Imports call() {
this.blockBuilder.addLineWithEnding("import org.junit.jupiter.api.Disabled");
return this;
}
@Override
public boolean accept() {
return this.generatedClassMetaData.configProperties
.getTestFramework() == TestFramework.JUNIT5
&& this.generatedClassMetaData.listOfFiles.stream()
.anyMatch(metadata -> metadata.isIgnored()
|| metadata.getConvertedContractWithMetadata().stream()
.anyMatch(SingleContractMetadata::isIgnored));
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import java.util.Arrays;
import org.springframework.cloud.contract.verifier.config.TestFramework;
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
class JUnit5IgnoreMethodAnnotation implements MethodAnnotations {
private final BlockBuilder blockBuilder;
private final GeneratedClassMetaData generatedClassMetaData;
private static final String[] ANNOTATIONS = { "@Disabled" };
JUnit5IgnoreMethodAnnotation(BlockBuilder blockBuilder,
GeneratedClassMetaData generatedClassMetaData) {
this.blockBuilder = blockBuilder;
this.generatedClassMetaData = generatedClassMetaData;
}
@Override
public MethodVisitor<MethodAnnotations> apply(
SingleContractMetadata singleContractMetadata) {
Arrays.stream(ANNOTATIONS).forEach(this.blockBuilder::addIndented);
return this;
}
@Override
public boolean accept(SingleContractMetadata singleContractMetadata) {
return this.generatedClassMetaData.configProperties
.getTestFramework() == TestFramework.JUNIT5
&& this.generatedClassMetaData.isAnyIgnored();
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import java.util.Arrays;
import org.springframework.cloud.contract.verifier.config.TestFramework;
class JUnit5Imports implements Imports {
private final BlockBuilder blockBuilder;
private final GeneratedClassMetaData generatedClassMetaData;
private static final String[] IMPORTS = { "org.junit.jupiter.api.Test",
"org.junit.jupiter.api.extension.ExtendWith" };
JUnit5Imports(BlockBuilder blockBuilder,
GeneratedClassMetaData generatedClassMetaData) {
this.blockBuilder = blockBuilder;
this.generatedClassMetaData = generatedClassMetaData;
}
@Override
public Imports call() {
Arrays.stream(IMPORTS)
.forEach(s -> this.blockBuilder.addLineWithEnding("import " + s));
return this;
}
@Override
public boolean accept() {
return this.generatedClassMetaData.configProperties
.getTestFramework() == TestFramework.JUNIT5;
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import java.util.Arrays;
import org.springframework.cloud.contract.verifier.config.TestFramework;
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
class JUnit5MethodAnnotation implements MethodAnnotations {
private final BlockBuilder blockBuilder;
private final GeneratedClassMetaData generatedClassMetaData;
private static final String[] ANNOTATIONS = { "@Test" };
JUnit5MethodAnnotation(BlockBuilder blockBuilder,
GeneratedClassMetaData generatedClassMetaData) {
this.blockBuilder = blockBuilder;
this.generatedClassMetaData = generatedClassMetaData;
}
@Override
public MethodVisitor<MethodAnnotations> apply(
SingleContractMetadata singleContractMetadata) {
Arrays.stream(ANNOTATIONS).forEach(this.blockBuilder::addIndented);
return this;
}
@Override
public boolean accept(SingleContractMetadata singleContractMetadata) {
return this.generatedClassMetaData.configProperties
.getTestFramework() == TestFramework.JUNIT5;
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import org.springframework.cloud.contract.verifier.config.TestFramework;
class JUnit5OrderClassAnnotation implements ClassAnnotation {
private final BlockBuilder blockBuilder;
private final GeneratedClassMetaData generatedClassMetaData;
private static final String[] ANNOTATIONS = {
"@FixMethodOrder(MethodSorters.NAME_ASCENDING)" };
JUnit5OrderClassAnnotation(BlockBuilder blockBuilder,
GeneratedClassMetaData generatedClassMetaData) {
this.blockBuilder = blockBuilder;
this.generatedClassMetaData = generatedClassMetaData;
}
@Override
public ClassAnnotation call() {
// Arrays.stream(ANNOTATIONS).forEach(this.blockBuilder::addIndented);
throw new UnsupportedOperationException(
"Not implemented yet in JUnit5 - https://github.com/junit-team/junit5/issues/48");
}
@Override
public boolean accept() {
return this.generatedClassMetaData.configProperties
.getTestFramework() == TestFramework.JUNIT5
&& this.generatedClassMetaData.listOfFiles.stream()
.anyMatch(meta -> meta.getOrder() != null);
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import org.springframework.cloud.contract.verifier.config.TestFramework;
class JUnit5OrderImports implements Imports {
private final BlockBuilder blockBuilder;
private final GeneratedClassMetaData generatedClassMetaData;
private static final String[] IMPORTS = { "" };
JUnit5OrderImports(BlockBuilder blockBuilder,
GeneratedClassMetaData generatedClassMetaData) {
this.blockBuilder = blockBuilder;
this.generatedClassMetaData = generatedClassMetaData;
}
@Override
public Imports call() {
// Arrays.stream(IMPORTS)
// .forEach(s -> this.blockBuilder.addLineWithEnding("import " + s));
throw new UnsupportedOperationException(
"Not implemented yet in JUnit5 - https://github.com/junit-team/junit5/issues/48");
}
@Override
public boolean accept() {
return this.generatedClassMetaData.configProperties
.getTestFramework() == TestFramework.JUNIT5
&& this.generatedClassMetaData.listOfFiles.stream()
.anyMatch(meta -> meta.getOrder() != null);
}
}

View File

@@ -1,292 +0,0 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder
import java.util.regex.Pattern
import groovy.json.StringEscapeUtils
import groovy.transform.PackageScope
import groovy.transform.TypeChecked
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.spec.internal.Cookie
import org.springframework.cloud.contract.spec.internal.ExecutionProperty
import org.springframework.cloud.contract.spec.internal.FromFileProperty
import org.springframework.cloud.contract.spec.internal.Header
import org.springframework.cloud.contract.spec.internal.Input
import org.springframework.cloud.contract.spec.internal.NamedProperty
import org.springframework.cloud.contract.spec.internal.NotToEscapePattern
import org.springframework.cloud.contract.spec.internal.RegexProperty
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
import org.springframework.cloud.contract.verifier.util.MapConverter
import static groovy.json.StringEscapeUtils.escapeJava
import static org.springframework.cloud.contract.verifier.config.TestFramework.JUNIT
/**
* Builds a JUnit method for messaging
*
* Do not change to {@code @CompileStatic} since it's using double dispatch.
*
* @author Marcin Grzejszczak
* @author Jakub Kubrynski, codearte.io
* @author Tim Ysewyn
*
* @since 1.0.0
*/
@PackageScope
@TypeChecked
class JUnitMessagingMethodBodyBuilder extends MessagingMethodBodyBuilder {
JUnitMessagingMethodBodyBuilder(Contract stubDefinition,
ContractVerifierConfigProperties configProperties,
GeneratedClassDataForMethod classDataForMethod) {
super(stubDefinition, configProperties, classDataForMethod)
}
@Override
protected String getInputString(Input request) {
if (request.triggeredBy) {
return request.triggeredBy.executionCommand
}
return "contractVerifierMessaging.send(inputMessage, \"${request.messageFrom.serverValue}\")"
}
@Override
protected String getResponseBodyPropertyComparisonString(String property, Object value) {
return ""
}
@Override
protected String getResponseBodyPropertyComparisonString(String property, String value) {
return ""
}
@Override
protected String getResponseBodyPropertyComparisonString(String property, Pattern value) {
return ""
}
@Override
protected String getResponseBodyPropertyComparisonString(String property, ExecutionProperty value) {
return ""
}
@Override
protected String getResponseBodyPropertyComparisonString(String property, FromFileProperty value) {
if (value.isByte()) {
return "assertThat(response.getPayloadAsByteArray()).isEqualTo(" +
readBytesFromFileString(value, CommunicationType.RESPONSE) + ")"
}
return getResponseBodyPropertyComparisonString(property, value.asString())
}
@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, property + "." + entry.key, entry.value)
}
@Override
protected void processHeaderElement(BlockBuilder blockBuilder, String property, String value) {
blockBuilder.addLine("assertThat(response.getHeader(\"$property\")).isNotNull();")
blockBuilder.
addLine("assertThat(response.getHeader(\"$property\").toString()).${createHeaderComparison(value)}")
}
@Override
protected void processHeaderElement(BlockBuilder blockBuilder, String property, Number value) {
blockBuilder.addLine("assertThat(response.getHeader(\"$property\")).isNotNull();")
blockBuilder.
addLine("assertThat(response.getHeader(\"$property\")).isEqualTo(${value});")
}
@Override
protected void processHeaderElement(BlockBuilder blockBuilder, String property, Pattern pattern) {
blockBuilder.addLine("assertThat(response.getHeader(\"$property\")).isNotNull();")
blockBuilder.
addLine("assertThat(response.getHeader(\"$property\").toString()).${createHeaderComparison(pattern)}")
}
@Override
protected void processHeaderElement(BlockBuilder blockBuilder, String property, ExecutionProperty exec) {
blockBuilder.addLine("assertThat(response.getHeader(\"$property\")).isNotNull();")
blockBuilder.
addLine("${exec.insertValue("response.getHeader(\"$property\").toString()")};")
}
@Override
protected void processCookieElement(BlockBuilder blockBuilder, String key, String value) {
}
@Override
protected void processCookieElement(BlockBuilder blockBuilder, String key, GString value) {
}
@Override
protected void processCookieElement(BlockBuilder blockBuilder, String key, Pattern pattern) {
}
@Override
protected void validateResponseCodeBlock(BlockBuilder bb) {
}
@Override
protected void validateResponseHeadersBlock(BlockBuilder bb) {
bb.addLine("""ContractVerifierMessage response = contractVerifierMessaging.receive(${
sentToValue(outputMessage.sentTo.serverValue)
});""")
bb.addLine("""assertThat(response).isNotNull();""")
outputMessage.headers?.executeForEachHeader { Header header ->
processHeaderElement(bb, header.name, header.serverValue instanceof NotToEscapePattern ?
header.serverValue :
MapConverter.getTestSideValues(header.serverValue))
}
}
@Override
protected void validateResponseCookiesBlock(BlockBuilder bb) {
}
private String sentToValue(Object sentTo) {
if (sentTo instanceof ExecutionProperty) {
return ((ExecutionProperty) sentTo).executionCommand
}
return '"' + sentTo.toString() + '"'
}
@Override
protected String getResponseAsString() {
return 'contractVerifierObjectMapper.writeValueAsString(response.getPayload())'
}
@Override
protected String addCommentSignIfRequired(String baseString) {
return "// $baseString"
}
@Override
protected boolean shouldCommentOutBDDBlocks() {
return true
}
@Override
protected BlockBuilder addColonIfRequired(BlockBuilder blockBuilder) {
blockBuilder.addAtTheEnd(JUNIT.lineSuffix)
return blockBuilder
}
@Override
protected Optional<String> lineSuffix() {
return Optional.of(JUNIT.lineSuffix)
}
@Override
protected String getPropertyInListString(String property, Integer listIndex) {
return "$property[$listIndex]" ?: ''
}
@Override
protected String convertUnicodeEscapesIfRequired(String json) {
return StringEscapeUtils.unescapeJavaScript(json)
}
@Override
protected String getSimpleResponseBodyString(String responseString) {
return "Object responseBody = ($responseString);"
}
@Override
protected String getInputString() {
String request = 'ContractVerifierMessage inputMessage = contractVerifierMessaging.create('
if (inputMessage.messageBody) {
request = "${request}${getBodyAsString()}"
}
if (inputMessage.messageHeaders) {
request = "${request}${indentHeadersString()}"
}
inputMessage.messageHeaders?.executeForEachHeader { Header header ->
request = "${request}${indentedHeaderString(header)}"
}
return finishIndentation(request)
}
private String indentHeadersString() {
return "\t\t\t\t, headers()"
}
private String indentedHeaderString(Header header) {
return "\n\t\t\t\t\t\t${getHeaderString(header)}"
}
private String finishIndentation(String text) {
return "${text}\n\t\t\t)"
}
@Override
protected String getHeaderString(Header header) {
return ".header(${getTestSideValue(header.name)}, ${getTestSideValue(header.serverValue)})"
}
@Override
protected String getCookieString(Cookie cookie) {
return ""
}
@Override
protected String getBodyString(Object body) {
return ""
}
@Override
protected String getMultipartFileParameterContent(String propertyName, NamedProperty propertyValue) {
return ""
}
@Override
protected String getParameterString(Map.Entry<String, Object> parameter) {
return ""
}
protected String convertHeaderComparison(String headerValue) {
return ""
}
protected String convertHeaderComparison(Pattern headerValue) {
return ""
}
protected String convertHeaderComparison(RegexProperty headerValue) {
return convertHeaderComparison(headerValue.pattern)
}
protected String createHeaderComparison(Object headerValue) {
String escapedHeader = convertUnicodeEscapesIfRequired("$headerValue")
return "isEqualTo(\"$escapedHeader\");"
}
protected String createHeaderComparison(Pattern headerValue) {
String escapedJavaHeader = escapeJava(headerValue.pattern())
return "matches(\"$escapedJavaHeader\");"
}
}

View File

@@ -1,244 +0,0 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder
import java.util.regex.Pattern
import groovy.json.StringEscapeUtils
import groovy.transform.PackageScope
import groovy.transform.TypeChecked
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.spec.internal.Cookie
import org.springframework.cloud.contract.spec.internal.ExecutionProperty
import org.springframework.cloud.contract.spec.internal.FromFileProperty
import org.springframework.cloud.contract.spec.internal.Header
import org.springframework.cloud.contract.spec.internal.NamedProperty
import org.springframework.cloud.contract.spec.internal.Request
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
import org.springframework.cloud.contract.verifier.util.RegexpBuilders
import static groovy.json.StringEscapeUtils.escapeJava
import static org.apache.commons.text.StringEscapeUtils.escapeJava
import static org.springframework.cloud.contract.verifier.config.TestFramework.JUNIT
import static org.springframework.cloud.contract.verifier.config.TestFramework.JUNIT5
import static org.springframework.cloud.contract.verifier.util.ContentUtils.getJavaMultipartFileParameterContent
/**
* Root class for JUnit method building
*
* Do not change to {@code @CompileStatic} since it's using double dispatch.
*
* @author Jakub Kubrynski, codearte.io
* @author Olga Maciaszek-Sharma, codearte.io
*
* @since 1.0.0
*/
@TypeChecked
@PackageScope
abstract class JUnitMethodBodyBuilder extends RequestProcessingMethodBodyBuilder {
JUnitMethodBodyBuilder(Contract stubDefinition, ContractVerifierConfigProperties configProperties,
GeneratedClassDataForMethod classDataForMethod) {
super(stubDefinition, configProperties, classDataForMethod)
}
@Override
protected String getResponseAsString() {
return "response.getBody().asString()"
}
@Override
protected String addCommentSignIfRequired(String baseString) {
return "// $baseString"
}
@Override
protected boolean shouldCommentOutBDDBlocks() {
return true
}
@Override
protected BlockBuilder addColonIfRequired(BlockBuilder blockBuilder) {
blockBuilder.addAtTheEnd(JUNIT.lineSuffix)
return blockBuilder
}
@Override
protected Optional<String> lineSuffix() {
return Optional.of(JUNIT5.lineSuffix)
}
@Override
protected String getResponseBodyPropertyComparisonString(String property, String value) {
return "assertThat(responseBody${property}).isEqualTo(\"${value}\")"
}
@Override
protected String getResponseBodyPropertyComparisonString(String property, Object value) {
return getResponseBodyPropertyComparisonString(property, value as String)
}
@Override
protected String getResponseBodyPropertyComparisonString(String property, Pattern value) {
return "assertThat(responseBody${property}).${createBodyComparison(value)}"
}
@Override
protected String getResponseBodyPropertyComparisonString(String property, ExecutionProperty value) {
return value.insertValue("responseBody${property}")
}
@Override
protected void processBodyElement(BlockBuilder blockBuilder, String property, ExecutionProperty exec) {
blockBuilder.addLine("${exec.insertValue("parsedJson.read(\"\$$property\")")};")
}
@Override
protected String getPropertyInListString(String property, Integer listIndex) {
return "${property}[$listIndex]" ?: ''
}
@Override
protected String convertUnicodeEscapesIfRequired(String json) {
String unescapedJson = StringEscapeUtils.unescapeJavaScript(json)
return escapeJava(unescapedJson)
}
@Override
protected void processBodyElement(BlockBuilder blockBuilder, String property, Map.Entry entry) {
processBodyElement(blockBuilder, property,
getMapKeyReferenceString(property, entry), entry.value)
}
private String getMapKeyReferenceString(String property, Map.Entry entry) {
return provideProperJsonPathNotation(property) + "." + entry.key
}
private String provideProperJsonPathNotation(String property) {
return property.replaceAll('(get\\(\\\\")(.*)(\\\\"\\))', '$2')
}
@Override
protected String getResponseBodyPropertyComparisonString(String property, FromFileProperty value) {
if (value.isByte()) {
return "assertThat(response.getBody().asByteArray()).isEqualTo(" +
readBytesFromFileString(value, CommunicationType.RESPONSE) + ")"
}
return getResponseBodyPropertyComparisonString(property, value.asString())
}
@Override
protected String getSimpleResponseBodyString(String responseString) {
return "String responseBody = $responseString;"
}
@Override
protected String getInputString(Request request) {
return "${returnedResponseType()} response = given().spec(request)"
}
protected String returnedResponseType() {
return "ResponseOptions"
}
@Override
protected String getInputString() {
return "${returnedRequestType()} request = given()"
}
protected String returnedRequestType() {
return "MockMvcRequestSpecification"
}
@Override
protected String getHeaderString(Header header) {
return ".header(${getTestSideForNonBodyValue(header.name)}, ${getTestSideForNonBodyValue(header.serverValue)})"
}
@Override
protected String getCookieString(Cookie cookie) {
return ".cookie(${getTestSideForNonBodyValue(cookie.key)}, ${getTestSideForNonBodyValue(cookie.serverValue)})"
}
@Override
protected String getBodyString(Object body) {
String value
if (body instanceof ExecutionProperty) {
value = body.toString()
}
else if (body instanceof FromFileProperty) {
FromFileProperty fileProperty = (FromFileProperty) body
value = fileProperty.isByte() ?
readBytesFromFileString(fileProperty, CommunicationType.REQUEST) :
readStringFromFileString(fileProperty, CommunicationType.REQUEST)
}
else {
String escaped = escapeRequestSpecialChars(body.toString())
value = "\"$escaped\""
}
return ".body($value)"
}
@Override
protected String getMultipartFileParameterContent(String propertyName, NamedProperty propertyValue) {
return getJavaMultipartFileParameterContent(propertyName, propertyValue, { FromFileProperty fileProp ->
readBytesFromFileString(fileProp, CommunicationType.REQUEST)
})
}
@Override
protected String getParameterString(Map.Entry<String, Object> parameter) {
return """.param("${escapeJava(parameter.key)}", "${
escapeJava(parameter.value as String)
}")"""
}
protected String createHeaderComparison(Object headerValue) {
String escapedHeader = convertUnicodeEscapesIfRequired("$headerValue")
return "isEqualTo(\"$escapedHeader\");"
}
protected String createHeaderComparison(Pattern headerValue) {
return createMatchesMethod(escapeJava(headerValue.pattern())) + ";"
}
protected String createBodyComparison(Pattern bodyValue) {
String patternAsString = bodyValue.pattern()
return createMatchesMethod(RegexpBuilders.
buildGStringRegexpForTestSide(patternAsString)) + ";"
}
protected String createCookieComparison(Object cookieValue) {
String escapedCookie = convertUnicodeEscapesIfRequired("$cookieValue")
return "isEqualTo(\"$escapedCookie\");"
}
protected String createCookieComparison(Pattern cookieValue) {
return createMatchesMethod(escapeJava(cookieValue.pattern())) + ";"
}
private String buildEscapedMatchesMethod(Pattern escapedValue) {
String escapedHeader = convertUnicodeEscapesIfRequired("$escapedValue")
return createMatchesMethod(escapedHeader)
}
protected String createMatchesMethod(String pattern) {
return "matches(\"$pattern\")"
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import org.springframework.cloud.contract.verifier.config.TestFramework;
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
class JUnitMethodMetadata implements MethodMetadata {
private final BlockBuilder blockBuilder;
private final GeneratedClassMetaData metaData;
private final NameProvider nameProvider = new NameProvider();
JUnitMethodMetadata(BlockBuilder blockBuilder, GeneratedClassMetaData metaData) {
this.blockBuilder = blockBuilder;
this.metaData = metaData;
}
@Override
public MethodMetadata name(SingleContractMetadata metaData) {
this.blockBuilder.addAtTheEnd(this.nameProvider.methodName(metaData));
return this;
}
@Override
public MethodMetadata modifier() {
this.blockBuilder.addIndented("public");
return this;
}
@Override
public MethodMetadata returnType() {
this.blockBuilder.append("void");
return this;
}
@Override
public boolean accept() {
return this.metaData.configProperties.getTestFramework() != TestFramework.SPOCK;
}
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import org.springframework.cloud.contract.verifier.config.TestFramework;
import org.springframework.util.StringUtils;
class JavaClassMetaData implements ClassMetaData, DefaultClassMetadata {
private final BlockBuilder blockBuilder;
private final BaseClassProvider baseClassProvider = new BaseClassProvider();
private final GeneratedClassMetaData generatedClassMetaData;
JavaClassMetaData(BlockBuilder blockBuilder,
GeneratedClassMetaData generatedClassMetaData) {
this.blockBuilder = blockBuilder;
this.generatedClassMetaData = generatedClassMetaData;
}
@Override
public ClassMetaData modifier() {
this.blockBuilder.append("public");
return this;
}
@Override
public ClassMetaData suffix() {
String suffix = StringUtils.hasText(
this.generatedClassMetaData.configProperties.getNameSuffixForTests())
? this.generatedClassMetaData.configProperties
.getNameSuffixForTests()
: "Test";
if (!this.blockBuilder.endsWith(suffix)) {
this.blockBuilder.addAtTheEnd(suffix);
}
return this;
}
@Override
public ClassMetaData setupLineEnding() {
this.blockBuilder.setupLineEnding(";");
return this;
}
@Override
public ClassMetaData setupLabelPrefix() {
this.blockBuilder.setupLabelPrefix("// ");
return this;
}
@Override
public GeneratedClassMetaData generatedClassMetaData() {
return this.generatedClassMetaData;
}
@Override
public BaseClassProvider baseClassProvider() {
return this.baseClassProvider;
}
@Override
public BlockBuilder blockBuilder() {
return this.blockBuilder;
}
@Override
public ClassMetaData parentClass() {
String baseClass = fqnBaseClass();
if (StringUtils.hasText(baseClass)) {
int lastIndexOf = baseClass.lastIndexOf(".");
if (lastIndexOf > 0) {
baseClass = baseClass.substring(lastIndexOf + 1);
}
blockBuilder().append("extends ").append(baseClass).append(" ");
}
return this;
}
@Override
public boolean accept() {
return this.generatedClassMetaData.configProperties
.getTestFramework() == TestFramework.JUNIT
|| this.generatedClassMetaData.configProperties
.getTestFramework() == TestFramework.JUNIT5;
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import org.springframework.cloud.contract.verifier.config.TestFramework;
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
class JavaJaxRsThen extends JaxRsThen {
private final GeneratedClassMetaData metaData;
JavaJaxRsThen(BlockBuilder blockBuilder,
GeneratedClassMetaData generatedClassMetaData) {
super(blockBuilder, generatedClassMetaData, JaxRsBodyParser.INSTANCE,
ComparisonBuilder.JAVA_HTTP_INSTANCE);
this.metaData = generatedClassMetaData;
}
@Override
public boolean accept(SingleContractMetadata singleContractMetadata) {
return super.accept(singleContractMetadata) && this.metaData.configProperties
.getTestFramework() != TestFramework.SPOCK;
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import org.springframework.cloud.contract.verifier.config.TestFramework;
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
class JavaJaxRsWhen extends JaxRsWhen {
private final GeneratedClassMetaData metaData;
JavaJaxRsWhen(BlockBuilder blockBuilder,
GeneratedClassMetaData generatedClassMetaData) {
super(blockBuilder, generatedClassMetaData, JaxRsBodyParser.INSTANCE);
this.metaData = generatedClassMetaData;
}
@Override
public boolean accept(SingleContractMetadata singleContractMetadata) {
return super.accept(singleContractMetadata) && this.metaData.configProperties
.getTestFramework() != TestFramework.SPOCK;
}
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
interface JavaMessagingBodyParser extends MessagingBodyParser {
BodyParser INSTANCE = new JavaMessagingBodyParser() {
};
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import org.springframework.cloud.contract.verifier.config.TestFramework;
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
class JavaMessagingGiven extends MessagingGiven {
private final GeneratedClassMetaData generatedClassMetaData;
JavaMessagingGiven(BlockBuilder blockBuilder,
GeneratedClassMetaData generatedClassMetaData) {
super(blockBuilder, generatedClassMetaData, JavaMessagingBodyParser.INSTANCE);
this.generatedClassMetaData = generatedClassMetaData;
}
@Override
public boolean accept(SingleContractMetadata metadata) {
return super.accept(metadata) && this.generatedClassMetaData.configProperties
.getTestFramework() != TestFramework.SPOCK;
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import org.springframework.cloud.contract.verifier.config.TestFramework;
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
class JavaMessagingWithBodyThen extends MessagingWithBodyThen {
private final GeneratedClassMetaData metaData;
JavaMessagingWithBodyThen(BlockBuilder blockBuilder,
GeneratedClassMetaData generatedClassMetaData) {
super(blockBuilder, generatedClassMetaData,
ComparisonBuilder.JAVA_MESSAGING_INSTANCE);
this.metaData = generatedClassMetaData;
}
@Override
public boolean accept(SingleContractMetadata singleContractMetadata) {
return super.accept(singleContractMetadata) && this.metaData.configProperties
.getTestFramework() != TestFramework.SPOCK;
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import org.springframework.cloud.contract.verifier.config.TestFramework;
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
class JavaRestAssuredGiven extends RestAssuredGiven {
private final GeneratedClassMetaData metaData;
JavaRestAssuredGiven(BlockBuilder blockBuilder,
GeneratedClassMetaData generatedClassMetaData) {
super(blockBuilder, generatedClassMetaData, RestAssuredBodyParser.INSTANCE);
this.metaData = generatedClassMetaData;
}
@Override
public boolean accept(SingleContractMetadata singleContractMetadata) {
return super.accept(singleContractMetadata) && this.metaData.configProperties
.getTestFramework() != TestFramework.SPOCK;
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import org.springframework.cloud.contract.verifier.config.TestFramework;
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
class JavaRestAssuredThen extends RestAssuredThen {
private final GeneratedClassMetaData metaData;
JavaRestAssuredThen(BlockBuilder blockBuilder,
GeneratedClassMetaData generatedClassMetaData) {
super(blockBuilder, generatedClassMetaData, RestAssuredBodyParser.INSTANCE,
ComparisonBuilder.JAVA_HTTP_INSTANCE);
this.metaData = generatedClassMetaData;
}
@Override
public boolean accept(SingleContractMetadata singleContractMetadata) {
return super.accept(singleContractMetadata) && this.metaData.configProperties
.getTestFramework() != TestFramework.SPOCK;
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import org.springframework.cloud.contract.verifier.config.TestFramework;
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
class JavaRestAssuredWhen extends RestAssuredWhen {
private final GeneratedClassMetaData metaData;
JavaRestAssuredWhen(BlockBuilder blockBuilder,
GeneratedClassMetaData generatedClassMetaData) {
super(blockBuilder, generatedClassMetaData, RestAssuredBodyParser.INSTANCE);
this.metaData = generatedClassMetaData;
}
@Override
public boolean accept(SingleContractMetadata singleContractMetadata) {
return super.accept(singleContractMetadata) && this.metaData.configProperties
.getTestFramework() != TestFramework.SPOCK;
}
}

View File

@@ -1,232 +0,0 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder
import java.lang.invoke.MethodHandles
import groovy.transform.Canonical
import groovy.transform.CompileStatic
import groovy.transform.EqualsAndHashCode
import groovy.transform.PackageScope
import org.apache.commons.logging.Log
import org.apache.commons.logging.LogFactory
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.verifier.builder.imports.HttpImportProvider
import org.springframework.cloud.contract.verifier.builder.imports.MessagingImportProvider
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
import org.springframework.cloud.contract.verifier.file.ContractMetadata
import static org.springframework.cloud.contract.verifier.builder.imports.BaseImportProvider.getImports
import static org.springframework.cloud.contract.verifier.builder.imports.BaseImportProvider.getRuleImport
import static org.springframework.cloud.contract.verifier.builder.imports.BaseImportProvider.getStaticImports
import static org.springframework.cloud.contract.verifier.util.NamesUtil.capitalize
/**
* Builds a single test for the given {@link ContractVerifierConfigProperties properties}
*
* @since 1.1.0
*/
@CompileStatic
class JavaTestGenerator implements SingleTestGenerator {
private static final Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass())
private static final String JSON_ASSERT_STATIC_IMPORT = 'com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson'
private static final String JSON_ASSERT_CLASS = 'com.toomuchcoding.jsonassert.JsonAssertion'
@Deprecated
// TODO: Remove in next major
private static final String REST_ASSURED_2_0_CLASS = 'com.jayway.restassured.RestAssured'
@PackageScope
ClassPresenceChecker checker = new ClassPresenceChecker()
@Override
String buildClass(ContractVerifierConfigProperties configProperties, Collection<ContractMetadata> listOfFiles, String includedDirectoryRelativePath, GeneratedClassData generatedClassData) {
String className = generatedClassData.className
String classPackage = generatedClassData.classPackage
ClassBuilder clazz = ClassBuilder.createClass(
capitalize(className), classPackage, configProperties, includedDirectoryRelativePath)
if (configProperties.imports) {
configProperties.imports.each { String it ->
clazz.addImport(it)
}
}
if (configProperties.staticImports) {
configProperties.staticImports.each { String it ->
clazz.addStaticImport(it)
}
}
if (isScenarioClass(listOfFiles)) {
clazz.addImports(configProperties.testFramework.getOrderAnnotationImports())
clazz.addClassLevelAnnotation(configProperties.testFramework.
getOrderAnnotation())
}
// FIXME: change during Hoxton refactoring: we should only add either Json or Xml imports and not both
addJsonPathRelatedImports(clazz)
addXPathRelatedImports(clazz)
processContractFiles(listOfFiles, configProperties, clazz, generatedClassData)
return clazz.build()
}
@Override
String buildClass(ContractVerifierConfigProperties configProperties, Collection<ContractMetadata> listOfFiles, String className, String classPackage, String includedDirectoryRelativePath) {
return buildClass(configProperties, listOfFiles, includedDirectoryRelativePath, new GeneratedClassData(className, classPackage, null))
}
private void processContractFiles(Collection<ContractMetadata> listOfFiles, ContractVerifierConfigProperties configProperties, ClassBuilder clazz, GeneratedClassData generatedClassData) {
Map<ParsedDsl, TestType> contracts = mapContractsToTheirTestTypes(listOfFiles)
boolean conditionalImportsAdded = false
boolean toIgnore = listOfFiles.find { it.ignored }
contracts.each { ParsedDsl key, TestType value ->
if (!conditionalImportsAdded) {
clazz.addImports(getImports(configProperties.testFramework))
clazz.addStaticImports(getStaticImports(configProperties.testFramework))
if (contracts.values().contains(TestType.HTTP)) {
addHttpRelatedEntries(clazz, configProperties)
}
if (configProperties.ruleClassForTests) {
addRule(configProperties, clazz)
}
if (contracts.values().contains(TestType.MESSAGING)) {
addMessagingRelatedEntries(clazz)
}
conditionalImportsAdded = true
}
toIgnore = toIgnore ? true : key.groovyDsl.ignored
clazz.addMethod(MethodBuilder.createTestMethod(key.contract, key.stubsFile,
key.groovyDsl, configProperties, generatedClassData))
}
if (toIgnore) {
clazz.addImport(configProperties.testFramework.getIgnoreClass())
}
}
private void addRule(ContractVerifierConfigProperties configProperties, ClassBuilder clazz) {
clazz.addImport(getRuleImport(configProperties.testFramework))
if (configProperties.testFramework.annotationLevelRules()) {
clazz.addClassLevelAnnotation(configProperties.testFramework
.getRuleAnnotation(configProperties.ruleClassForTests))
}
else {
clazz.addRule(configProperties.ruleClassForTests)
}
}
private void addHttpRelatedEntries(ClassBuilder clazz, ContractVerifierConfigProperties configProperties) {
HttpImportProvider httpImportProvider = new HttpImportProvider(
getRestAssuredPackage())
clazz.addImports(httpImportProvider.
getImports(configProperties.testFramework, configProperties.testMode))
clazz.addStaticImports(httpImportProvider.
getStaticImports(configProperties.testFramework, configProperties.testMode))
}
// TODO for 2.2: leave only RestAssured 3
private String getRestAssuredPackage() {
boolean restAssured2Present = this.checker.isClassPresent(REST_ASSURED_2_0_CLASS)
String restAssuredPackage = restAssured2Present ? 'com.jayway.restassured' : 'io.restassured'
if (log.isDebugEnabled()) {
log.debug("Rest Assured version 2.x found [${restAssured2Present}]")
}
return restAssuredPackage
}
@Override
String fileExtension(ContractVerifierConfigProperties properties) {
return properties.testFramework.classExtension
}
private Map<ParsedDsl, TestType> mapContractsToTheirTestTypes(Collection<ContractMetadata> listOfFiles) {
Map<ParsedDsl, TestType> dsls = [:]
listOfFiles.each { ContractMetadata metadata ->
File stubsFile = metadata.path.toFile()
if (log.isDebugEnabled()) {
log.debug("Stub content from file [${stubsFile.text}]")
}
Collection<Contract> stubContents = metadata.convertedContract
Map<ParsedDsl, TestType> entries = stubContents.
collectEntries { Contract stubContent ->
TestType testType = (stubContent.input || stubContent.outputMessage) ? TestType.MESSAGING : TestType.HTTP
return [(new ParsedDsl(metadata, stubContent, stubsFile)): testType]
}
dsls.putAll(entries)
}
return dsls
}
@Canonical
@EqualsAndHashCode(includeFields = true)
@CompileStatic
private static class ParsedDsl {
ContractMetadata contract
Contract groovyDsl
File stubsFile
}
private static enum TestType {
MESSAGING, HTTP
}
private boolean isScenarioClass(Collection<ContractMetadata> listOfFiles) {
return listOfFiles.find({ it.order != null }) != null
}
private void addJsonPathRelatedImports(ClassBuilder clazz) {
clazz.addImports(['com.jayway.jsonpath.DocumentContext',
'com.jayway.jsonpath.JsonPath',
])
if (this.checker.isClassPresent(JSON_ASSERT_CLASS)) {
clazz.addStaticImport(JSON_ASSERT_STATIC_IMPORT)
}
}
private void addXPathRelatedImports(ClassBuilder clazz) {
clazz.addImports(['javax.xml.parsers.DocumentBuilder',
'javax.xml.parsers.DocumentBuilderFactory',
'org.w3c.dom.Document',
'org.xml.sax.InputSource',
'java.io.StringReader'])
}
private void addMessagingRelatedEntries(ClassBuilder clazz) {
clazz.addField(['@Inject ContractVerifierMessaging contractVerifierMessaging',
'@Inject ContractVerifierObjectMapper contractVerifierObjectMapper'
])
clazz.addImports(MessagingImportProvider.getImports())
clazz.addStaticImports(MessagingImportProvider.getStaticImports())
}
}
class ClassPresenceChecker {
private static final Log log = LogFactory.getLog(ClassPresenceChecker)
boolean isClassPresent(String className) {
try {
Class.forName(className)
return true
}
catch (ClassNotFoundException ex) {
if (log.isDebugEnabled()) {
log.debug("[${className}] is not present on classpath. Will not add a static import.")
}
return false
}
}
}

View File

@@ -0,0 +1,126 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import java.util.Collection;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties;
import org.springframework.cloud.contract.verifier.file.ContractMetadata;
/**
* Builds a single test for the given {@link ContractVerifierConfigProperties properties}
*
* @since 1.1.0
*/
public class JavaTestGenerator implements SingleTestGenerator {
private static final Log log = LogFactory.getLog(JavaTestGenerator.class);
@Override
public String buildClass(ContractVerifierConfigProperties properties,
Collection<ContractMetadata> listOfFiles, String className,
String classPackage, String includedDirectoryRelativePath) {
throw new UnsupportedOperationException("Deprecated method");
}
@Override
public String buildClass(ContractVerifierConfigProperties properties,
Collection<ContractMetadata> listOfFiles,
String includedDirectoryRelativePath, GeneratedClassData generatedClassData) {
BlockBuilder builder = new BlockBuilder("\t");
GeneratedClassMetaData metaData = new GeneratedClassMetaData(properties,
listOfFiles, includedDirectoryRelativePath, generatedClassData);
return classAsString(builder, metaData);
}
private String classAsString(BlockBuilder builder, GeneratedClassMetaData metaData) {
SingleMethodBuilder methodBuilder = singleMethodBuilder(builder, metaData);
ClassBodyBuilder bodyBuilder = classBodyBuilder(builder, metaData, methodBuilder);
GeneratedTestClass generatedTestClass = generatedTestClass(builder, metaData,
bodyBuilder);
return generatedTestClass.asClassString();
}
GeneratedTestClass generatedTestClass(BlockBuilder builder,
GeneratedClassMetaData metaData, ClassBodyBuilder bodyBuilder) {
// @formatter:off
return GeneratedTestClassBuilder.builder(builder, metaData)
.classBodyBuilder(bodyBuilder)
.metaData()
.java()
.groovy()
.build()
.imports()
.defaultImports()
.custom()
.json()
.jUnit4()
.jUnit5()
.spock()
.xml()
.messaging()
.restAssured()
.jaxRs()
.build()
.classAnnotations()
.jUnit4()
.jUnit5()
.spock()
.build()
.build();
// @formatter:on
}
ClassBodyBuilder classBodyBuilder(BlockBuilder builder,
GeneratedClassMetaData metaData, SingleMethodBuilder methodBuilder) {
// @formatter:off
return ClassBodyBuilder.builder(builder, metaData)
.field()
.messaging()
.build()
.methodBuilder(methodBuilder);
// @formatter:on
}
SingleMethodBuilder singleMethodBuilder(BlockBuilder builder,
GeneratedClassMetaData metaData) {
// @formatter:off
return SingleMethodBuilder.builder(builder, metaData)
.methodAnnotation()
.jUnit4()
.jUnit5()
.spock()
.build()
.methodMetadata()
.jUnit()
.spock()
.build()
.restAssured()
.jaxRs()
.messaging();
// @formatter:on
}
@Override
public String fileExtension(ContractVerifierConfigProperties properties) {
return properties.getTestFramework().getClassExtension();
}
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import org.springframework.cloud.contract.verifier.config.TestMode;
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
interface JaxRsAcceptor {
default boolean acceptType(GeneratedClassMetaData generatedClassMetaData,
SingleContractMetadata singleContractMetadata) {
return generatedClassMetaData.configProperties
.getTestMode() == TestMode.JAXRSCLIENT && singleContractMetadata.isHttp();
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
interface JaxRsBodyParser extends BodyParser {
JaxRsBodyParser INSTANCE = new JaxRsBodyParser() {
};
default String readEntity() {
return "response.readEntity(String.class)";
}
default String responseAsString() {
return "responseAsString";
}
default String byteArrayString() {
return "response.readEntity(byte[].class)";
}
}

View File

@@ -1,265 +0,0 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder
import java.util.regex.Pattern
import groovy.transform.PackageScope
import groovy.transform.TypeChecked
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.spec.internal.Cookie
import org.springframework.cloud.contract.spec.internal.DslProperty
import org.springframework.cloud.contract.spec.internal.ExecutionProperty
import org.springframework.cloud.contract.spec.internal.FromFileProperty
import org.springframework.cloud.contract.spec.internal.Header
import org.springframework.cloud.contract.spec.internal.NotToEscapePattern
import org.springframework.cloud.contract.spec.internal.QueryParameter
import org.springframework.cloud.contract.spec.internal.QueryParameters
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
import org.springframework.cloud.contract.verifier.util.MapConverter
import static org.springframework.cloud.contract.verifier.config.TestFramework.JUNIT
/**
* JaxRs implementation of the {@link JUnitMethodBodyBuilder}. Knows how to build
* a test method for JaxRs.
*
* Do not change to {@code @CompileStatic} since it's using double dispatch.
*
* @author Olga Maciaszek-Sharma, codearte.io
*
* @since 1.0.0
*/
@TypeChecked
@PackageScope
class JaxRsClientJUnitMethodBodyBuilder extends JUnitMethodBodyBuilder {
JaxRsClientJUnitMethodBodyBuilder(Contract stubDefinition,
ContractVerifierConfigProperties configProperties,
GeneratedClassDataForMethod classDataForMethod) {
super(stubDefinition, configProperties, classDataForMethod)
}
@Override
protected void given(BlockBuilder bb) {}
@Override
protected void givenBlock(BlockBuilder bb) {
}
@Override
protected void when(BlockBuilder bb) {
bb.addLine("Response response = webTarget")
bb.indent()
appendUrlPathAndQueryParameters(bb)
appendRequestWithRequiredResponseContentType(bb)
appendHeaders(bb)
appendCookies(bb)
appendMethodAndBody(bb)
bb.addAtTheEnd(JUNIT.lineSuffix)
bb.unindent()
bb.addEmptyLine()
if (expectsResponseBody()) {
bb.addLine("String responseAsString = response.readEntity(String.class);")
}
}
protected void appendUrlPathAndQueryParameters(BlockBuilder bb) {
if (request.url) {
bb.addLine(".path(${concreteUrl(request.url)})")
appendQueryParams(request.url.queryParameters, bb)
}
else if (request.urlPath) {
bb.addLine(".path(${concreteUrl(request.urlPath)})")
appendQueryParams(request.urlPath.queryParameters, bb)
}
}
protected String concreteUrl(DslProperty url) {
Object testSideUrl = MapConverter.getTestSideValues(url)
if (!(testSideUrl instanceof ExecutionProperty)) {
return '"' + testSideUrl.toString() + '"'
}
return testSideUrl.toString()
}
private void appendQueryParams(QueryParameters queryParameters, BlockBuilder bb) {
if (!queryParameters?.parameters) {
return
}
queryParameters.parameters.findAll(this.&allowedQueryParameter).
each { QueryParameter param ->
bb.addLine(".queryParam(\"$param.name\", \"${resolveParamValue(param).toString()}\")")
}
}
@Override
protected String getResponseBodyPropertyComparisonString(String property, FromFileProperty value) {
if (value.isByte()) {
return "assertThat(response.readEntity(byte[].class)).isEqualTo(" +
readBytesFromFileString(value, CommunicationType.RESPONSE) + ")"
}
return getResponseBodyPropertyComparisonString(property, value.asString())
}
protected void appendMethodAndBody(BlockBuilder bb) {
String method = request.method.serverValue.toString().toLowerCase()
if (request.body) {
String contentType =
getHeader('Content-Type') ?: getRequestContentType().mimeType
Object body = request.body.serverValue
String value
if (body instanceof ExecutionProperty) {
value = body.toString()
}
else if (body instanceof FromFileProperty) {
FromFileProperty fileProperty = (FromFileProperty) body
value = fileProperty.isByte() ?
readBytesFromFileString(fileProperty, CommunicationType.REQUEST) :
readStringFromFileString(fileProperty, CommunicationType.REQUEST)
}
else {
value = "\"${getBodyAsString()}\""
}
bb.addLine(".method(\"${method.toUpperCase()}\", entity(${value}, \"$contentType\"))")
}
else {
bb.addLine(".method(\"${method.toUpperCase()}\")")
}
}
protected appendHeaders(BlockBuilder bb) {
request.headers?.executeForEachHeader { Header header ->
if (headerOfAbsentType(header)) {
return
}
if (header.name == 'Content-Type' || header.name == 'Accept') {
return
}
bb.addLine(".header(\"${header.name}\", ${quotedAndEscaped(header.serverValue as String)})")
}
}
protected appendCookies(BlockBuilder bb) {
request.cookies?.executeForEachCookie { Cookie cookie ->
if (cookieOfAbsentType(cookie)) {
return
}
bb.addLine(".cookie(\"${cookie.key}\", ${quotedAndEscaped(cookie.serverValue as String)})")
}
}
protected void appendRequestWithRequiredResponseContentType(BlockBuilder bb) {
String acceptHeader = getHeader("Accept")
if (acceptHeader) {
bb.addLine(".request(\"$acceptHeader\")")
}
else {
bb.addLine(".request()")
}
}
@Override
protected void validateResponseCodeBlock(BlockBuilder bb) {
bb.addLine("assertThat(response.getStatus()).isEqualTo($response.status.serverValue);")
}
@Override
protected void validateResponseHeadersBlock(BlockBuilder bb) {
response.headers?.executeForEachHeader { Header header ->
processHeaderElement(bb, header.name, header.serverValue instanceof NotToEscapePattern ?
header.serverValue :
MapConverter.getTestSideValues(header.serverValue))
}
}
@Override
protected void validateResponseCookiesBlock(BlockBuilder bb) {
response.cookies?.executeForEachCookie { Cookie cookie ->
processCookieElement(bb, cookie.key, cookie.serverValue instanceof NotToEscapePattern ?
cookie.serverValue :
MapConverter.getTestSideValues(cookie.serverValue))
}
}
protected String getHeader(String name) {
return request.headers?.entries?.find { it.name == name }?.serverValue
}
@Override
protected String getResponseAsString() {
return 'responseAsString'
}
@Override
protected void processHeaderElement(BlockBuilder blockBuilder, String property, Object value) {
if (value instanceof NotToEscapePattern) {
blockBuilder.
addLine("assertThat(response.getHeaderString(\"$property\")).${createHeaderComparison(((NotToEscapePattern) value).serverValue)}")
}
else {
// fallback
processHeaderElement(blockBuilder, property, value.toString())
}
}
@Override
protected void processHeaderElement(BlockBuilder blockBuilder, String property, String value) {
blockBuilder.
addLine("assertThat(response.getHeaderString(\"$property\")).${createHeaderComparison(value)}")
}
@Override
protected void processHeaderElement(BlockBuilder blockBuilder, String property, Number value) {
blockBuilder.
addLine("assertThat(response.getHeaderString(\"$property\")).isEqualTo(${value});")
}
@Override
protected void processHeaderElement(BlockBuilder blockBuilder, String property, Pattern pattern) {
blockBuilder.
addLine("assertThat(response.getHeaderString(\"$property\")).${createHeaderComparison(pattern)}")
}
@Override
protected void processHeaderElement(BlockBuilder blockBuilder, String property, ExecutionProperty exec) {
blockBuilder.
addLine("${exec.insertValue("response.getHeaderString(\"$property\")")};")
}
@Override
protected void processCookieElement(BlockBuilder blockBuilder, String key, Pattern pattern) {
blockBuilder.
addLine("assertThat(response.getCookies().get(\"$key\")).isNotNull();")
blockBuilder.
addLine("assertThat(response.getCookies().get(\"$key\").getValue()).${createCookieComparison(pattern)}")
}
@Override
protected void processCookieElement(BlockBuilder blockBuilder, String key, String value) {
blockBuilder.
addLine("assertThat(response.getCookies().get(\"$key\")).isNotNull();")
blockBuilder.
addLine("assertThat(response.getCookies().get(\"$key\").getValue()).${createCookieComparison(value)}")
}
}

View File

@@ -1,265 +0,0 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder
import java.util.regex.Pattern
import groovy.transform.PackageScope
import groovy.transform.TypeChecked
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.spec.internal.Cookie
import org.springframework.cloud.contract.spec.internal.DslProperty
import org.springframework.cloud.contract.spec.internal.ExecutionProperty
import org.springframework.cloud.contract.spec.internal.FromFileProperty
import org.springframework.cloud.contract.spec.internal.Header
import org.springframework.cloud.contract.spec.internal.NotToEscapePattern
import org.springframework.cloud.contract.spec.internal.QueryParameter
import org.springframework.cloud.contract.spec.internal.QueryParameters
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
import org.springframework.cloud.contract.verifier.util.MapConverter
/**
* Knows how to build a Spock test method for JaxRs.
*
* Do not change to {@code @CompileStatic} since it's using double dispatch.
*
* @author Olga Maciaszek-Sharma, codearte.io
*
* @since 1.0.0
*/
@PackageScope
@TypeChecked
class JaxRsClientSpockMethodRequestProcessingBodyBuilder extends SpockMethodRequestProcessingBodyBuilder {
JaxRsClientSpockMethodRequestProcessingBodyBuilder(Contract stubDefinition,
ContractVerifierConfigProperties configProperties,
GeneratedClassDataForMethod classDataForMethod) {
super(stubDefinition, configProperties, classDataForMethod)
}
@Override
protected void given(BlockBuilder bb) {}
@Override
protected void givenBlock(BlockBuilder bb) {
}
@Override
protected void when(BlockBuilder bb) {
bb.addLine("def response = webTarget")
bb.indent()
appendUrlPathAndQueryParameters(bb)
appendRequestWithRequiredResponseContentType(bb)
appendHeaders(bb)
appendCookies(bb)
appendMethodAndBody(bb)
bb.unindent()
bb.addEmptyLine()
if (expectsResponseBody()) {
bb.addLine("String responseAsString = response.readEntity(String)")
}
}
protected void appendRequestWithRequiredResponseContentType(BlockBuilder bb) {
String acceptHeader = getHeader("Accept")
if (acceptHeader) {
bb.addLine(".request('$acceptHeader')")
}
else {
bb.addLine(".request()")
}
}
protected void appendUrlPathAndQueryParameters(BlockBuilder bb) {
if (request.url) {
bb.addLine(".path(${concreteUrl(request.url)})")
appendQueryParams(request.url.queryParameters, bb)
}
else if (request.urlPath) {
bb.addLine(".path(${concreteUrl(request.urlPath)})")
appendQueryParams(request.urlPath.queryParameters, bb)
}
}
protected String concreteUrl(DslProperty url) {
Object testSideUrl = MapConverter.getTestSideValues(url)
if (!(testSideUrl instanceof ExecutionProperty)) {
return "'" + testSideUrl.toString() + "'"
}
return testSideUrl.toString()
}
private void appendQueryParams(QueryParameters queryParameters, BlockBuilder bb) {
if (!queryParameters?.parameters) {
return
}
queryParameters.parameters.findAll(this.&allowedQueryParameter).
each { QueryParameter param ->
bb.addLine(".queryParam('$param.name', '${resolveParamValue(param).toString()}')")
}
}
@Override
protected String getResponseBodyPropertyComparisonString(String property, FromFileProperty value) {
if (value.isByte()) {
return "response.readEntity(byte[]) == " +
readBytesFromFileString(value, CommunicationType.RESPONSE)
}
return getResponseBodyPropertyComparisonString(property, value.asString())
}
protected void appendMethodAndBody(BlockBuilder bb) {
String method = request.method.serverValue.toString().toLowerCase()
if (request.body) {
String contentType =
getHeader('Content-Type') ?: getRequestContentType().mimeType
Object body = request.body.serverValue
String value
if (body instanceof ExecutionProperty) {
value = body.toString()
}
else if (body instanceof FromFileProperty) {
FromFileProperty fileProperty = (FromFileProperty) body
value = fileProperty.isByte() ?
readBytesFromFileString(fileProperty, CommunicationType.REQUEST) :
readStringFromFileString(fileProperty, CommunicationType.REQUEST)
}
else {
value = "'${bodyAsString}'"
}
bb.addLine(".method('${method.toUpperCase()}', entity(${value}, '$contentType'))")
}
else {
bb.addLine(".method('${method.toUpperCase()}')")
}
}
protected appendHeaders(BlockBuilder bb) {
request.headers?.executeForEachHeader { Header header ->
if (headerOfAbsentType(header)) {
return
}
if (header.name == 'Content-Type' || header.name == 'Accept') {
return
} // Particular headers are set via 'request' / 'entity' methods
bb.addLine(".header('${header.name}', '${header.serverValue}')")
}
}
protected appendCookies(BlockBuilder bb) {
request.cookies?.executeForEachCookie { Cookie cookie ->
if (cookieOfAbsentType(cookie)) {
return
}
bb.addLine(".cookie('${cookie.key}', '${cookie.serverValue}')")
}
}
protected String getHeader(String name) {
return request.headers?.entries?.find { it.name == name }?.serverValue
}
@Override
protected void validateResponseCodeBlock(BlockBuilder bb) {
bb.addLine("response.status == $response.status.serverValue")
}
@Override
protected void validateResponseHeadersBlock(BlockBuilder bb) {
response.headers?.executeForEachHeader { Header header ->
processHeaderElement(bb, header.name, header.serverValue instanceof NotToEscapePattern ?
header.serverValue :
MapConverter.getTestSideValues(header.serverValue))
}
}
@Override
protected void validateResponseCookiesBlock(BlockBuilder bb) {
response.cookies?.executeForEachCookie { Cookie cookie ->
processCookieElement(bb, cookie.key, cookie.serverValue instanceof NotToEscapePattern ?
cookie.serverValue :
MapConverter.getTestSideValues(cookie.serverValue))
}
}
@Override
protected String getResponseAsString() {
return 'responseAsString'
}
@Override
protected void processHeaderElement(BlockBuilder blockBuilder, String property, Object value) {
if (value instanceof NotToEscapePattern) {
blockBuilder.
addLine("response.getHeaderString('$property') ${convertHeaderComparison(((NotToEscapePattern) value).serverValue)}")
}
else {
// fallback
processHeaderElement(blockBuilder, property, value.toString())
}
}
@Override
protected void processHeaderElement(BlockBuilder blockBuilder, String property, ExecutionProperty exec) {
blockBuilder.
addLine("${exec.insertValue("response.getHeaderString(\'$property\')")}")
}
@Override
protected void processHeaderElement(BlockBuilder blockBuilder, String property, String value) {
blockBuilder.
addLine("response.getHeaderString('$property') ${convertHeaderComparison(value)}")
}
@Override
protected void processHeaderElement(BlockBuilder blockBuilder, String property, Number value) {
blockBuilder.addLine("response.getHeaderString('$property') == ${value}")
}
@Override
protected void processHeaderElement(BlockBuilder blockBuilder, String property, Pattern value) {
blockBuilder.
addLine("response.getHeaderString('$property') ${convertHeaderComparison(value)}")
}
@Override
protected void processCookieElement(BlockBuilder blockBuilder, String key, Pattern pattern) {
blockBuilder.addLine("response.getCookies().get('$key') != null")
blockBuilder.
addLine("response.getCookies().get('$key').getValue() ${convertCookieComparison(pattern)}")
}
@Override
protected void processCookieElement(BlockBuilder blockBuilder, String key, String value) {
blockBuilder.addLine("response.getCookies().get('$key') != null")
blockBuilder.
addLine("response.getCookies().get('$key').getValue() ${convertCookieComparison(value)}")
}
@Override
protected String postProcessJsonPathCall(String jsonPath) {
if (templateProcessor.containsTemplateEntry(jsonPath)) {
return jsonPath
}
return jsonPath.replace('$', '\\$')
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
class JaxRsGiven implements Given, JaxRsAcceptor {
private final GeneratedClassMetaData generatedClassMetaData;
JaxRsGiven(GeneratedClassMetaData metaData) {
this.generatedClassMetaData = metaData;
}
@Override
public MethodVisitor<Given> apply(SingleContractMetadata metadata) {
return this;
}
@Override
public boolean accept(SingleContractMetadata metadata) {
return acceptType(this.generatedClassMetaData, metadata);
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import java.util.Arrays;
import org.springframework.cloud.contract.verifier.config.TestMode;
class JaxRsImports implements Imports {
private final BlockBuilder blockBuilder;
private final GeneratedClassMetaData generatedClassMetaData;
private static final String[] IMPORTS = { "javax.ws.rs.client.Entity",
"javax.ws.rs.core.Response" };
JaxRsImports(BlockBuilder blockBuilder,
GeneratedClassMetaData generatedClassMetaData) {
this.blockBuilder = blockBuilder;
this.generatedClassMetaData = generatedClassMetaData;
}
@Override
public Imports call() {
Arrays.stream(IMPORTS)
.forEach(s -> this.blockBuilder.addLineWithEnding("import " + s));
return this;
}
@Override
public boolean accept() {
return this.generatedClassMetaData.configProperties
.getTestMode() == TestMode.JAXRSCLIENT;
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import java.util.Iterator;
import java.util.Set;
import org.springframework.cloud.contract.spec.internal.Cookie;
import org.springframework.cloud.contract.spec.internal.MatchingStrategy;
import org.springframework.cloud.contract.spec.internal.Request;
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
class JaxRsRequestCookiesWhen implements When {
private final BlockBuilder blockBuilder;
private final BodyParser bodyParser;
JaxRsRequestCookiesWhen(BlockBuilder blockBuilder, BodyParser bodyParser) {
this.blockBuilder = blockBuilder;
this.bodyParser = bodyParser;
}
@Override
public MethodVisitor<When> apply(SingleContractMetadata metadata) {
appendCookies(metadata.getContract().getRequest());
return this;
}
private void appendCookies(Request request) {
Iterator<Cookie> iterator = request.getCookies().getEntries().stream()
.filter(cookie -> !cookieOfAbsentType(cookie)).iterator();
while (iterator.hasNext()) {
Cookie cookie = iterator.next();
String value = ".cookie(" + this.bodyParser.quotedShortText(cookie.getKey())
+ ", " + this.bodyParser.quotedShortText(cookie.getServerValue())
+ ")";
if (iterator.hasNext()) {
this.blockBuilder.addLine(value);
}
else {
this.blockBuilder.addIndented(value);
}
}
}
private boolean cookieOfAbsentType(Cookie cookie) {
return cookie.getServerValue() instanceof MatchingStrategy
&& ((MatchingStrategy) cookie.getServerValue())
.getType() == MatchingStrategy.Type.ABSENT;
}
@Override
public boolean accept(SingleContractMetadata metadata) {
return metadata.getContract().getRequest().getCookies() != null && !metadata
.getContract().getRequest().getCookies().getEntries().isEmpty()
&& !hasOnlyAbsentCookies(metadata);
}
private boolean hasOnlyAbsentCookies(SingleContractMetadata metadata) {
Set<Cookie> entries = metadata.getContract().getRequest().getCookies()
.getEntries();
long filteredOut = entries.stream().filter(this::cookieOfAbsentType).count();
return filteredOut == entries.size();
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import java.util.Iterator;
import java.util.Set;
import org.springframework.cloud.contract.spec.internal.Header;
import org.springframework.cloud.contract.spec.internal.MatchingStrategy;
import org.springframework.cloud.contract.spec.internal.Request;
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
class JaxRsRequestHeadersWhen implements When {
private final BlockBuilder blockBuilder;
private final BodyParser bodyParser;
JaxRsRequestHeadersWhen(BlockBuilder blockBuilder, BodyParser bodyParser) {
this.blockBuilder = blockBuilder;
this.bodyParser = bodyParser;
}
@Override
public MethodVisitor<When> apply(SingleContractMetadata metadata) {
appendHeaders(metadata.getContract().getRequest());
return this;
}
private void appendHeaders(Request request) {
Iterator<Header> iterator = request.getHeaders().getEntries().stream()
.filter(header -> !headerToIgnore(header)).iterator();
while (iterator.hasNext()) {
Header header = iterator.next();
String text = ".header(\"" + header.getName() + "\", "
+ this.bodyParser.quotedLongText(header.getServerValue()) + ")";
if (iterator.hasNext()) {
this.blockBuilder.addLine(text);
}
else {
this.blockBuilder.addIndented(text);
}
}
}
private boolean headerToIgnore(Header header) {
return contentTypeOrAccept(header) || headerOfAbsentType(header);
}
private boolean contentTypeOrAccept(Header header) {
return "Content-Type".equalsIgnoreCase(header.getName())
|| "Accept".equalsIgnoreCase(header.getName());
}
private boolean headerOfAbsentType(Header header) {
return header.getServerValue() instanceof MatchingStrategy
&& ((MatchingStrategy) header.getServerValue())
.getType() == MatchingStrategy.Type.ABSENT;
}
@Override
public boolean accept(SingleContractMetadata metadata) {
return metadata.getContract().getRequest().getHeaders() != null && !metadata
.getContract().getRequest().getHeaders().getEntries().isEmpty()
&& !hasHeaderOnlyContentTypeOrAccept(metadata);
}
private boolean hasHeaderOnlyContentTypeOrAccept(SingleContractMetadata metadata) {
Set<Header> entries = metadata.getContract().getRequest().getHeaders()
.getEntries();
long filteredOut = entries.stream().filter(this::headerToIgnore).count();
return filteredOut == entries.size();
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
class JaxRsRequestInvokerWhen implements When, JaxRsBodyParser {
private final BlockBuilder blockBuilder;
JaxRsRequestInvokerWhen(BlockBuilder blockBuilder) {
this.blockBuilder = blockBuilder;
}
@Override
public MethodVisitor<When> apply(SingleContractMetadata metadata) {
this.blockBuilder.addIndented(".invoke()").addEndingIfNotPresent();
return this;
}
@Override
public boolean accept(SingleContractMetadata metadata) {
return true;
}
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import org.springframework.cloud.contract.spec.internal.ExecutionProperty;
import org.springframework.cloud.contract.spec.internal.FromFileProperty;
import org.springframework.cloud.contract.spec.internal.Header;
import org.springframework.cloud.contract.spec.internal.Request;
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
import org.springframework.cloud.contract.verifier.util.ContentType;
import org.springframework.util.StringUtils;
class JaxRsRequestMethodWhen implements When, JaxRsBodyParser {
private final BlockBuilder blockBuilder;
private final BodyReader bodyReader;
JaxRsRequestMethodWhen(BlockBuilder blockBuilder, GeneratedClassMetaData metaData) {
this.blockBuilder = blockBuilder;
this.bodyReader = new BodyReader(metaData);
}
@Override
public MethodVisitor<When> apply(SingleContractMetadata metadata) {
appendMethodAndBody(metadata);
return this;
}
void appendMethodAndBody(SingleContractMetadata metadata) {
Request request = metadata.getContract().getRequest();
ContentType type = metadata.getInputTestContentType();
String method = request.getMethod().getServerValue().toString().toLowerCase();
if (request.getBody() != null) {
String contentType = type.getMimeType();
contentType = StringUtils.hasText(contentType) ? contentType
: getContentType(request);
Object body = request.getBody().getServerValue();
String value;
if (body instanceof ExecutionProperty) {
value = body.toString();
}
else if (body instanceof FromFileProperty) {
FromFileProperty fileProperty = (FromFileProperty) body;
value = fileProperty.isByte()
? this.bodyReader.readBytesFromFileString(metadata, fileProperty,
CommunicationType.REQUEST)
: this.bodyReader.readStringFromFileString(metadata, fileProperty,
CommunicationType.REQUEST);
}
else {
value = "\"" + requestBodyAsString(metadata) + "\"";
}
this.blockBuilder.addIndented(".build(\"" + method.toUpperCase()
+ "\", entity(" + value + ", \"" + contentType + "\"))");
}
else {
this.blockBuilder.addIndented(".build(\"" + method.toUpperCase() + "\")");
}
}
private String getContentType(Request request) {
Header contentType = request.getHeaders().getEntries().stream()
.filter(header -> "Content-Type".equalsIgnoreCase(header.getName()))
.findFirst().orElse(null);
return contentType != null ? contentType.getServerValue().toString() : "";
}
@Override
public boolean accept(SingleContractMetadata metadata) {
return true;
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import org.springframework.cloud.contract.spec.internal.Header;
import org.springframework.cloud.contract.spec.internal.Request;
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
import org.springframework.util.StringUtils;
class JaxRsRequestWhen implements When, JaxRsAcceptor, QueryParamsResolver {
private final BlockBuilder blockBuilder;
private final GeneratedClassMetaData generatedClassMetaData;
JaxRsRequestWhen(BlockBuilder blockBuilder, GeneratedClassMetaData metaData) {
this.blockBuilder = blockBuilder;
this.generatedClassMetaData = metaData;
}
@Override
public MethodVisitor<When> apply(SingleContractMetadata metadata) {
appendRequestWithRequiredResponseContentType(metadata.getContract().getRequest());
return this;
}
void appendRequestWithRequiredResponseContentType(Request request) {
String acceptHeader = getHeader(request, "Accept");
if (StringUtils.hasText(acceptHeader)) {
this.blockBuilder.addIndented(".request(\"" + acceptHeader + "\")");
}
else {
this.blockBuilder.addIndented(".request()");
}
}
private String getHeader(Request request, String name) {
if (request.getHeaders() == null || request.getHeaders().getEntries() == null) {
return "";
}
Header foundHeader = request.getHeaders().getEntries().stream()
.filter(header -> name.equals(header.getName())).findFirst().orElse(null);
if (foundHeader == null) {
return "";
}
return foundHeader.getServerValue().toString();
}
@Override
public boolean accept(SingleContractMetadata metadata) {
return acceptType(this.generatedClassMetaData, metadata);
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import org.springframework.cloud.contract.spec.internal.Response;
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
class JaxRsResponseCookiesThen implements Then, MockMvcAcceptor, CookieElementProcessor {
private final BlockBuilder blockBuilder;
private final GeneratedClassMetaData generatedClassMetaData;
private final ComparisonBuilder comparisonBuilder;
private final BodyParser bodyParser;
JaxRsResponseCookiesThen(BlockBuilder blockBuilder,
GeneratedClassMetaData generatedClassMetaData,
ComparisonBuilder comparisonBuilder) {
this.blockBuilder = blockBuilder;
this.generatedClassMetaData = generatedClassMetaData;
this.comparisonBuilder = comparisonBuilder;
this.bodyParser = comparisonBuilder.bodyParser();
}
@Override
public MethodVisitor<Then> apply(SingleContractMetadata metadata) {
processCookies(metadata);
return this;
}
@Override
public ComparisonBuilder comparisonBuilder() {
return this.comparisonBuilder;
}
@Override
public BlockBuilder blockBuilder() {
return this.blockBuilder;
}
@Override
public String cookieKey(String key) {
return "response.getCookies().get(" + this.bodyParser.quotedShortText(key) + ")";
}
@Override
public String cookieValue(String key) {
return cookieKey(key) + ".getValue()";
}
@Override
public boolean accept(SingleContractMetadata metadata) {
Response response = metadata.getContract().getResponse();
return response.getCookies() != null;
}
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.builder;
import java.util.Iterator;
import org.springframework.cloud.contract.spec.internal.ExecutionProperty;
import org.springframework.cloud.contract.spec.internal.Header;
import org.springframework.cloud.contract.spec.internal.Headers;
import org.springframework.cloud.contract.spec.internal.NotToEscapePattern;
import org.springframework.cloud.contract.spec.internal.Response;
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
import org.springframework.cloud.contract.verifier.util.MapConverter;
class JaxRsResponseHeadersThen implements Then {
private final BlockBuilder blockBuilder;
private final GeneratedClassMetaData generatedClassMetaData;
private final ComparisonBuilder comparisonBuilder;
JaxRsResponseHeadersThen(BlockBuilder blockBuilder, GeneratedClassMetaData metaData,
ComparisonBuilder comparisonBuilder) {
this.blockBuilder = blockBuilder;
this.generatedClassMetaData = metaData;
this.comparisonBuilder = comparisonBuilder;
}
@Override
public MethodVisitor<Then> apply(SingleContractMetadata metadata) {
validateResponseHeadersBlock(metadata);
return this;
}
private void validateResponseHeadersBlock(SingleContractMetadata metadata) {
Response response = metadata.getContract().getResponse();
Headers headers = response.getHeaders();
Iterator<Header> iterator = headers.getEntries().iterator();
while (iterator.hasNext()) {
Header header = iterator.next();
String text = processHeaderElement(header.getName(),
header.getServerValue() instanceof NotToEscapePattern
? header.getServerValue()
: MapConverter.getTestSideValues(header.getServerValue()));
if (iterator.hasNext()) {
this.blockBuilder.addLineWithEnding(text);
}
else {
this.blockBuilder.addIndented(text);
}
}
}
private String processHeaderElement(String property, Object value) {
if (value instanceof NotToEscapePattern) {
return this.comparisonBuilder
.assertThat("response.getHeaderString(\"" + property + "\")")
+ this.comparisonBuilder.createComparison(
((NotToEscapePattern) value).getServerValue());
}
else if (value instanceof Number) {
return this.comparisonBuilder
.assertThat("response.getHeaderString(\"" + property + "\")", value);
}
else if (value instanceof ExecutionProperty) {
return ((ExecutionProperty) value)
.insertValue("response.getHeaderString(\"" + property + "\")");
}
else {
return this.comparisonBuilder
.assertThat("response.getHeaderString(\"" + property + "\")")
+ this.comparisonBuilder.createComparison(value);
}
}
@Override
public boolean accept(SingleContractMetadata metadata) {
return metadata.getContract().getResponse().getHeaders() != null;
}
}

Some files were not shown because too many files have changed in this diff Show More