From cf44bdd73b7fd50abe616a3e17fc1b3feabf1acc Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Fri, 8 Nov 2019 12:51:22 +0100 Subject: [PATCH] Making inProgress not generate a single test method instead of the whole test class fixes gh-1260 --- .gitignore | 3 +- .../InProgressContractMethodPreProcessor.java | 33 ++++++++ .../verifier/builder/MethodPreProcessor.java | 36 ++++++++ .../verifier/builder/SingleMethodBuilder.java | 35 +++++++- .../verifier/file/ContractMetadata.groovy | 4 + .../SpringTestMethodBodyBuildersSpec.groovy | 84 ++++++++++++++++++- 6 files changed, 188 insertions(+), 7 deletions(-) create mode 100644 spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/InProgressContractMethodPreProcessor.java create mode 100644 spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodPreProcessor.java diff --git a/.gitignore b/.gitignore index 798a979058..052b287229 100644 --- a/.gitignore +++ b/.gitignore @@ -42,4 +42,5 @@ interpolated-settings.xml interpolated-pom.xml dependency-reduced-pom.xml -.vscode/ \ No newline at end of file +.vscode/ +.flattened-pom.xml diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/InProgressContractMethodPreProcessor.java b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/InProgressContractMethodPreProcessor.java new file mode 100644 index 0000000000..ba30f0284d --- /dev/null +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/InProgressContractMethodPreProcessor.java @@ -0,0 +1,33 @@ +/* + * 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 InProgressContractMethodPreProcessor implements MethodPreProcessor { + + @Override + public boolean shouldContinue() { + return false; + } + + @Override + public boolean accept(SingleContractMetadata metadata) { + return metadata.isInProgress(); + } + +} diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodPreProcessor.java b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodPreProcessor.java new file mode 100644 index 0000000000..78b55e9720 --- /dev/null +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodPreProcessor.java @@ -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; + +import org.springframework.cloud.contract.verifier.file.SingleContractMetadata; + +interface MethodPreProcessor extends MethodVisitor { + + @Override + default MethodVisitor apply( + SingleContractMetadata singleContractMetadata) { + return this; + } + + /** + * @return {@code true} if method should be processed + */ + default boolean shouldContinue() { + return true; + } + +} diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/SingleMethodBuilder.java b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/SingleMethodBuilder.java index 3494d58454..224f064e87 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/SingleMethodBuilder.java +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/SingleMethodBuilder.java @@ -22,6 +22,9 @@ import java.util.LinkedList; import java.util.List; import java.util.stream.Collectors; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + import org.springframework.cloud.contract.verifier.file.SingleContractMetadata; /** @@ -33,10 +36,14 @@ import org.springframework.cloud.contract.verifier.file.SingleContractMetadata; */ class SingleMethodBuilder { + private static final Log log = LogFactory.getLog(SingleMethodBuilder.class); + private List methodAnnotations = new LinkedList<>(); private List methodMetadata = new LinkedList<>(); + private List methodPreProcessors = new LinkedList<>(); + private List methodPostProcessors = new LinkedList<>(); private List givens = new LinkedList<>(); @@ -86,6 +93,7 @@ class SingleMethodBuilder { SingleMethodBuilder restAssured() { return given( new JavaRestAssuredGiven(this.blockBuilder, this.generatedClassMetaData)) + .methodPreProcessor(new InProgressContractMethodPreProcessor()) .given(new SpockRestAssuredGiven(this.blockBuilder, this.generatedClassMetaData)) .when(new JavaRestAssuredWhen(this.blockBuilder, @@ -101,7 +109,8 @@ class SingleMethodBuilder { } SingleMethodBuilder jaxRs() { - return given(new JaxRsGiven(this.generatedClassMetaData)) + return methodPreProcessor(new InProgressContractMethodPreProcessor()) + .given(new JaxRsGiven(this.generatedClassMetaData)) .when(new JavaJaxRsWhen(this.blockBuilder, this.generatedClassMetaData)) .when(new SpockJaxRsWhen(this.blockBuilder, this.generatedClassMetaData)) .then(new JavaJaxRsThen(this.blockBuilder, this.generatedClassMetaData)) @@ -112,7 +121,8 @@ class SingleMethodBuilder { SingleMethodBuilder messaging() { // @formatter:off - return given(new JavaMessagingGiven(this.blockBuilder, this.generatedClassMetaData)) + return methodPreProcessor(new InProgressContractMethodPreProcessor()) + .given(new JavaMessagingGiven(this.blockBuilder, this.generatedClassMetaData)) .given(new SpockMessagingGiven(this.blockBuilder, this.generatedClassMetaData)) .when(new MessagingWhen(this.blockBuilder, this.generatedClassMetaData)) .then(new JavaMessagingWithBodyThen(this.blockBuilder, @@ -140,6 +150,11 @@ class SingleMethodBuilder { return this; } + SingleMethodBuilder methodPreProcessor(MethodPreProcessor methodPreProcessor) { + this.methodPreProcessors.add(methodPreProcessor); + return this; + } + SingleMethodBuilder methodPostProcessor(MethodPostProcessor methodPostProcessor) { this.methodPostProcessors.add(methodPostProcessor); return this; @@ -154,6 +169,14 @@ class SingleMethodBuilder { // \n this.blockBuilder.addEmptyLine(); this.generatedClassMetaData.toSingleContractMetadata().forEach(metaData -> { + boolean stopProcessing = shouldStopProcessing(metaData); + if (stopProcessing) { + if (log.isDebugEnabled()) { + log.debug("The method for meta data [" + metaData + + "] will not be processed further. At least one method pre-processor declared that this method should be skipped."); + } + return; + } // @Test if (visit(this.methodAnnotations, metaData, false)) { this.blockBuilder.addEmptyLine(); @@ -187,6 +210,14 @@ class SingleMethodBuilder { return this.blockBuilder; } + private boolean shouldStopProcessing(SingleContractMetadata metaData) { + List matchingPreProcessors = this.methodPreProcessors.stream() + .filter(m -> m.accept(metaData)) + .collect(Collectors.toCollection(LinkedList::new)); + matchingPreProcessors.forEach(m -> m.apply(metaData)); + return matchingPreProcessors.stream().anyMatch(m -> !m.shouldContinue()); + } + private MethodMetadata pickMetadatum() { return this.methodMetadata.stream().filter(Acceptor::accept).findFirst() .orElseThrow(() -> new IllegalStateException( diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/file/ContractMetadata.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/file/ContractMetadata.groovy index 25239502f4..7038f06ee8 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/file/ContractMetadata.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/file/ContractMetadata.groovy @@ -181,6 +181,10 @@ class SingleContractMetadata { return this.http } + boolean isInProgress() { + return this.contract.isInProgress() + } + boolean isMessaging() { return !isHttp() } diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/SpringTestMethodBodyBuildersSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/SpringTestMethodBodyBuildersSpec.groovy index ef18833794..9373c37594 100644 --- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/SpringTestMethodBodyBuildersSpec.groovy +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/SpringTestMethodBodyBuildersSpec.groovy @@ -219,6 +219,10 @@ class SpringTestMethodBodyBuildersSpec extends Specification implements WireMock } private String singleTestGenerator(Contract contractDsl) { + return singleTestGenerator([contractDsl]) + } + + private String singleTestGenerator(Collection contractDsls) { return new JavaTestGenerator() { @Override ClassBodyBuilder classBodyBuilder(BlockBuilder builder, GeneratedClassMetaData metaData, SingleMethodBuilder methodBuilder) { @@ -235,15 +239,15 @@ class SpringTestMethodBodyBuildersSpec extends Specification implements WireMock } }) } - }.buildClass(properties, [contractMetadata(contractDsl)], "foo", generatedClassData) + }.buildClass(properties, [contractMetadata(contractDsls)], "foo", generatedClassData) } private GeneratedClassMetaData generatedClassMetaData(Contract contractDsl) { - new GeneratedClassMetaData(properties, [contractMetadata(contractDsl)], "foo", generatedClassData) + new GeneratedClassMetaData(properties, [contractMetadata([contractDsl])], "foo", generatedClassData) } - ContractMetadata contractMetadata(Contract contractDsl) { - return new ContractMetadata(new File(".").toPath(), false, 0, null, contractDsl) + ContractMetadata contractMetadata(Collection contractDsls) { + return new ContractMetadata(new File(".").toPath(), false, 0, null, contractDsls) } @Issue('#187') @@ -3054,4 +3058,76 @@ DocumentContext parsedJson = JsonPath.parse(json); } "webclient" | { properties.testMode = TestMode.WEBTESTCLIENT } } + + @Issue('#1260') + def 'should generate test methods for not in progress contracts [#methodBuilderName]'() { + given: + List contractDsl = [ + Contract.make { + name("httpContractNotInProgress") + request { + method PUT() + url '/httpContractNotInProgress' + } + response { + status OK() + } + }, + Contract.make { + name("httpContractInProgress") + inProgress() + request { + method PUT() + url '/httpContractInProgress' + } + response { + status OK() + } + }, + Contract.make { + name("messagingContractNotInProgress") + input { + triggeredBy("toString()") + } + outputMessage { + sentTo("messagingContractNotInProgress") + body([type: "messagingContractNotInProgress"]) + } + }, + Contract.make { + name("messagingContractInProgress") + inProgress() + input { + triggeredBy("toString()") + } + outputMessage { + sentTo("messagingContractNotInProgress") + body([type: "messagingContractNotInProgress"]) + } + } + ] + methodBuilder() + when: + String test = singleTestGenerator(contractDsl) + then: + SyntaxChecker.tryToCompile(methodBuilderName, test) + and: + test.contains('''httpContractNotInProgress''') + test.contains('''messagingContractNotInProgress''') + and: + !test.contains('''httpContractInProgress''') + !test.contains('''messagingContractInProgress''') + where: + methodBuilderName | methodBuilder + "spock" | { properties.testFramework = TestFramework.SPOCK } + "testng" | { properties.testFramework = TestFramework.TESTNG } + "mockmvc" | { properties.testMode = TestMode.MOCKMVC } + "jaxrs-spock" | { + properties.testFramework = TestFramework.SPOCK; properties.testMode = TestMode.JAXRSCLIENT + } + "jaxrs" | { + properties.testFramework = TestFramework.JUNIT; properties.testMode = TestMode.JAXRSCLIENT + } + "webclient" | { properties.testMode = TestMode.WEBTESTCLIENT } + } }