Fixed equals and hashcode

This commit is contained in:
Marcin Grzejszczak
2016-12-06 17:22:52 +01:00
parent a328130418
commit 39bef2ce08
35 changed files with 567 additions and 111 deletions

View File

@@ -40,15 +40,43 @@ class Contract {
* take precedence
*/
Integer priority
/**
* The HTTP request part of the contract
*/
Request request
/**
* The HTTP response part of the contract
*/
Response response
/**
* The label by which you'll reference the contract on the message consumer side
*/
String label
/**
* Description of a contract. May be used in the documentation generation.
*/
String description
/**
* Name of the generated test / stub. If not provided then the file name will be used.
* If you have multiple contracts in a single file and you don't provide this value
* then a prefix will be added to the file with the index number while iterating
* over the collection of contracts.
*
* Remember to have a unique name for every single contract. Otherwise you might
* generate tests that have two identical methods or you will override the stubs.
*/
String name
/**
* The input side of a messaging contract.
*/
Input input
/**
* The output side of a messaging contract.
*/
OutputMessage outputMessage
/**
* Whether the contract should be ignored or not
* Whether the contract should be ignored or not.
*/
boolean ignored
@@ -68,6 +96,10 @@ class Contract {
this.priority = priority
}
void name(String name) {
this.name = name
}
void label(String label) {
this.label = label
}

View File

@@ -25,8 +25,8 @@ import groovy.transform.ToString
*
* @since 1.0.0
*/
@ToString(includePackage = false, includeFields = true, includeNames = true)
@EqualsAndHashCode(includeFields = true)
@ToString(includePackage = false, includeFields = true, includeNames = true, includeSuper = true)
@EqualsAndHashCode(callSuper = true)
@CompileStatic
class Body extends DslProperty {

View File

@@ -17,6 +17,8 @@
package org.springframework.cloud.contract.spec.internal
import groovy.transform.CompileStatic
import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString
/**
* Represents a client side {@link DslProperty}
@@ -24,6 +26,8 @@ import groovy.transform.CompileStatic
* @since 1.0.0
*/
@CompileStatic
@EqualsAndHashCode(callSuper = true)
@ToString(includeSuper = true)
class ClientDslProperty extends DslProperty {
ClientDslProperty(Object singleValue) {

View File

@@ -20,7 +20,6 @@ import groovy.transform.PackageScope
import groovy.transform.TypeChecked
import java.util.regex.Pattern
/**
* Contains useful common methods for the DSL.
*

View File

@@ -19,14 +19,13 @@ package org.springframework.cloud.contract.spec.internal
import groovy.transform.CompileStatic
import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString
/**
* Represents an element of a DSL that can contain client or sever side values
*
* @since 1.0.0
*/
@CompileStatic
@EqualsAndHashCode(includeFields = true)
@EqualsAndHashCode
@ToString(includePackage = false, includeNames = true)
class DslProperty<T> {

View File

@@ -17,6 +17,7 @@
package org.springframework.cloud.contract.spec.internal
import groovy.transform.CompileStatic
import groovy.transform.EqualsAndHashCode
/**
* Represents a property that will become an executable method in the
@@ -25,6 +26,7 @@ import groovy.transform.CompileStatic
* @since 1.0.0
*/
@CompileStatic
@EqualsAndHashCode
class ExecutionProperty {
private static final String PLACEHOLDER_VALUE = '\\$it'

View File

@@ -24,8 +24,8 @@ import groovy.transform.ToString
*
* @since 1.0.0
*/
@EqualsAndHashCode(includeFields = true)
@ToString(includePackage = false, includeFields = true, ignoreNulls = true, includeNames = true)
@EqualsAndHashCode(includeFields = true, callSuper = true)
@ToString(includePackage = false, includeFields = true, ignoreNulls = true, includeNames = true, includeSuper = true)
@CompileStatic
class Header extends DslProperty {

View File

@@ -11,7 +11,7 @@ import groovy.transform.ToString
* @since 1.0.2
*/
@CompileStatic
@EqualsAndHashCode
@EqualsAndHashCode(includeFields = true)
@ToString(includePackage = false)
class HttpMethods {

View File

@@ -88,6 +88,8 @@ class Input extends Common {
return new DslProperty(client.clientValue, clientValue)
}
@EqualsAndHashCode(includeFields = true, callSuper = true)
@ToString(includeSuper = true)
static class BodyType extends DslProperty {
BodyType(Object clientValue, Object serverValue) {

View File

@@ -25,8 +25,8 @@ import groovy.transform.ToString;
*
* @since 1.0.0
*/
@EqualsAndHashCode(includeFields = true)
@ToString(includePackage = false, includeFields = true, ignoreNulls = true, includeNames = true)
@EqualsAndHashCode(includeFields = true, callSuper = true)
@ToString(includePackage = false, includeFields = true, ignoreNulls = true, includeNames = true, includeSuper = true)
@CompileStatic
class MatchingStrategy extends DslProperty {

View File

@@ -20,8 +20,8 @@ import groovy.transform.CompileStatic
import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString
@ToString(includePackage = false, includeFields = true, includeNames = true)
@EqualsAndHashCode(includeFields = true)
@ToString(includePackage = false, includeFields = true, includeNames = true, includeSuper = true)
@EqualsAndHashCode(callSuper = true)
@CompileStatic
class Multipart extends DslProperty {

View File

@@ -4,7 +4,6 @@ import groovy.transform.CompileStatic
import groovy.transform.PackageScope
import java.util.regex.Pattern
/**
* @author Marcin Grzejszczak
*/

View File

@@ -27,8 +27,8 @@ import static org.springframework.cloud.contract.spec.util.ValidateUtils.validat
*
* @since 1.0.0
*/
@EqualsAndHashCode(includeFields = true)
@ToString(includePackage = false, includeFields = true, ignoreNulls = true, includeNames = true)
@EqualsAndHashCode(includeFields = true, callSuper = true)
@ToString(includePackage = false, includeFields = true, ignoreNulls = true, includeNames = true, includeSuper = true)
@CompileStatic
class QueryParameter extends DslProperty {

View File

@@ -213,7 +213,7 @@ class Request extends Common {
}
@CompileStatic
@EqualsAndHashCode
@EqualsAndHashCode(includeFields = true)
@ToString(includePackage = false)
private class ServerRequest extends Request {
ServerRequest(Request request) {
@@ -222,7 +222,7 @@ class Request extends Common {
}
@CompileStatic
@EqualsAndHashCode
@EqualsAndHashCode(includeFields = true)
@ToString(includePackage = false)
private class ClientRequest extends Request {
ClientRequest(Request request) {
@@ -231,7 +231,7 @@ class Request extends Common {
}
@CompileStatic
@EqualsAndHashCode
@EqualsAndHashCode(includeFields = true)
@ToString(includePackage = false)
private class RequestHeaders extends Headers {
@@ -243,7 +243,7 @@ class Request extends Common {
}
@CompileStatic
@EqualsAndHashCode
@EqualsAndHashCode(includeFields = true)
@ToString(includePackage = false)
private class ClientPatternValueDslProperty extends PatternValueDslProperty<ClientDslProperty> {

View File

@@ -30,7 +30,7 @@ import java.util.regex.Pattern
* @since 1.0.0
*/
@TypeChecked
@EqualsAndHashCode
@EqualsAndHashCode(includeFields = true)
@ToString(includePackage = false, includeFields = true)
class Response extends Common {
@@ -120,7 +120,7 @@ class Response extends Common {
}
@CompileStatic
@EqualsAndHashCode
@EqualsAndHashCode(callSuper = true)
@ToString(includePackage = false)
private class ServerResponse extends Response {
ServerResponse(Response request) {
@@ -129,7 +129,7 @@ class Response extends Common {
}
@CompileStatic
@EqualsAndHashCode
@EqualsAndHashCode(callSuper = true)
@ToString(includePackage = false)
private class ClientResponse extends Response {
ClientResponse(Response request) {
@@ -138,7 +138,7 @@ class Response extends Common {
}
@CompileStatic
@EqualsAndHashCode
@EqualsAndHashCode(includeFields = true)
@ToString(includePackage = false)
private class ResponseHeaders extends Headers {
@@ -150,7 +150,7 @@ class Response extends Common {
}
@CompileStatic
@EqualsAndHashCode
@EqualsAndHashCode(includeFields = true)
@ToString(includePackage = false)
private class ServerPatternValueDslProperty extends PatternValueDslProperty<ServerDslProperty> {

View File

@@ -26,8 +26,8 @@ import groovy.transform.ToString
* @since 1.0.0
*/
@CompileStatic
@EqualsAndHashCode(includeFields = true)
@ToString(includePackage = false)
@EqualsAndHashCode(callSuper = true)
@ToString(includePackage = false, includeSuper = true)
class ServerDslProperty extends DslProperty {
ServerDslProperty(Object singleValue) {

View File

@@ -27,8 +27,8 @@ import static org.springframework.cloud.contract.spec.util.ValidateUtils.validat
*
* @since 1.0.0
*/
@ToString(includePackage = false, includeFields = true, includeNames = true)
@EqualsAndHashCode(includeFields = true)
@ToString(includePackage = false, includeFields = true, includeNames = true, includeSuper = true)
@EqualsAndHashCode(includeFields = true, callSuper = true)
@CompileStatic
class Url extends DslProperty {

View File

@@ -25,8 +25,8 @@ import groovy.transform.ToString
*
* @since 1.0.0
*/
@ToString(includePackage = false, includeFields = true, includeNames = true)
@EqualsAndHashCode(includeFields = true)
@ToString(includePackage = false, includeFields = true, includeNames = true, includeSuper = true)
@EqualsAndHashCode(includeFields = true, callSuper = true)
@CompileStatic
class UrlPath extends Url {

View File

@@ -102,4 +102,154 @@ then:
}
// end::ignored[]
}
def 'should make equals and hashcode work properly for URL'() {
expect:
def a = Contract.make {
request {
url("/1")
}
}
def b = Contract.make {
request {
url("/1")
}
}
a == b
}
def 'should make equals and hashcode work properly for URL with consumer producer'() {
expect:
Contract.make {
request {
url($(c("/1"), p("/1")))
}
} == Contract.make {
request {
url($(c("/1"), p("/1")))
}
}
}
def 'should return true when comparing two equal contracts with gstring'() {
expect:
int index = 1
def a = Contract.make {
request {
method(PUT())
headers {
contentType(applicationJson())
}
url "/${index}"
}
response {
status 200
}
}
def b = Contract.make {
request {
method(PUT())
headers {
contentType(applicationJson())
}
url "/${index}"
}
response {
status 200
}
}
a == b
}
def 'should return false when comparing two unequal contracts with gstring'() {
expect:
int index = 1
def a = Contract.make {
request {
method(PUT())
headers {
contentType(applicationJson())
}
url "/${index}"
}
response {
status 200
}
}
int index2 = 2
def b = Contract.make {
request {
method(PUT())
headers {
contentType(applicationJson())
}
url "/${index2}"
}
response {
status 200
}
}
a != b
}
def 'should return true when comparing two equal complex contracts'() {
expect:
def a = Contract.make {
request {
method 'GET'
url '/path'
headers {
header('Accept': $(
consumer(regex('text/.*')),
producer('text/plain')
))
header('X-Custom-Header': $(
consumer(regex('^.*2134.*$')),
producer('121345')
))
}
}
response {
status 200
body(
id: [value: '132'],
surname: 'Kowalsky',
name: 'Jan',
created: '2014-02-02 12:23:43'
)
headers {
header 'Content-Type': 'text/plain'
}
}
}
def b = Contract.make {
request {
method 'GET'
url '/path'
headers {
header('Accept': $(
consumer(regex('text/.*')),
producer('text/plain')
))
header('X-Custom-Header': $(
consumer(regex('^.*2134.*$')),
producer('121345')
))
}
}
response {
status 200
body(
id: [value: '132'],
surname: 'Kowalsky',
name: 'Jan',
created: '2014-02-02 12:23:43'
)
headers {
header 'Content-Type': 'text/plain'
}
}
}
a == b
}
}

View File

@@ -131,7 +131,7 @@ class StubRepository {
File file = path.toFile();
if (isContractDescriptor(file)) {
mappingDescriptors
.add(ContractVerifierDslConverter.convert(file));
.addAll(ContractVerifierDslConverter.convertAsCollection(file));
}
return super.visitFile(path, attrs);
}

View File

@@ -17,6 +17,7 @@
package org.springframework.cloud.contract.verifier.converter
import groovy.transform.CompileStatic
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.verifier.file.ContractMetadata
/**
@@ -46,7 +47,9 @@ interface SingleFileConverter {
/**
* Returns the name of the converted stub file. If you have multiple contracts
* in a single file then a prefix will be added to the generated file.
* in a single file then a prefix will be added to the generated file. If you
* provide the {@link Contract#name} field then that field will override the
* generated file name.
*
* Example: name of file with 2 contracts is {@code foo.groovy}, it will be
* converted by the implementation to {@code foo.json}. The recursive file

View File

@@ -20,6 +20,7 @@ import groovy.transform.CompileStatic
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.verifier.dsl.wiremock.WireMockStubStrategy
import org.springframework.cloud.contract.verifier.file.ContractMetadata
import org.springframework.cloud.contract.verifier.util.NamesUtil
import java.nio.charset.StandardCharsets
@@ -49,7 +50,7 @@ class DslToWireMockClientConverter extends DslToWireMockConverter {
}
List<String> convertedContracts = []
contract.convertedContract.eachWithIndex { Contract dsl, int index ->
String name = "${rootName}_${index}"
String name = dsl.name ? NamesUtil.convertIllegalPackageChars(dsl.name) : "${rootName}_${index}"
convertedContracts << convertASingleContract(name, contract, dsl)
}
return convertedContracts

View File

@@ -47,7 +47,7 @@ abstract class DslToWireMockConverter implements SingleFileConverter {
return ""
}
protected List<Contract> createGroovyDSLFromStringContent(String groovyDslAsString) {
protected Collection<Contract> createGroovyDSLFromStringContent(String groovyDslAsString) {
return ContractVerifierDslConverter.convertAsCollection(groovyDslAsString)
}
}

View File

@@ -25,6 +25,7 @@ import org.springframework.cloud.contract.verifier.converter.SingleFileConverter
import org.springframework.cloud.contract.verifier.converter.SingleFileConvertersHolder
import org.springframework.cloud.contract.verifier.file.ContractFileScanner
import org.springframework.cloud.contract.verifier.file.ContractMetadata
import org.springframework.cloud.contract.verifier.util.NamesUtil
import java.nio.charset.StandardCharsets
import java.nio.file.Files
@@ -94,7 +95,7 @@ class RecursiveFilesConverter {
}
Path absoluteTargetPath = createAndReturnTargetDirectory(sourceFile)
File newJsonFile = createTargetFileWithProperName(singleFileConverter, absoluteTargetPath,
sourceFile, contractsSize, index)
sourceFile, contractsSize, index, dsl)
newJsonFile.setText(convertedContent, StandardCharsets.UTF_8.toString())
}
} catch (Exception e) {
@@ -112,11 +113,22 @@ class RecursiveFilesConverter {
}
private File createTargetFileWithProperName(SingleFileConverter singleFileConverter, Path absoluteTargetPath,
File sourceFile, int contractsSize, int index) {
String generatedName = singleFileConverter.generateOutputFileNameForInput(sourceFile.name)
String name = contractsSize == 1 ? generatedName : "${index}_${generatedName}"
File sourceFile, int contractsSize, int index, Contract dsl) {
String name = generateName(dsl, contractsSize, singleFileConverter, sourceFile, index)
File newJsonFile = new File(absoluteTargetPath.toFile(), name)
log.info("Creating new json [$newJsonFile.path]")
log.info("Creating new stub [$newJsonFile.path]")
return newJsonFile
}
private String generateName(Contract dsl, int contractsSize, SingleFileConverter converter,
File sourceFile, int index) {
String generatedName = converter.generateOutputFileNameForInput(sourceFile.name)
String extension = NamesUtil.afterLastDot(generatedName)
if (dsl.name) {
return "${dsl.name}.${extension}"
} else if (contractsSize == 1) {
return generatedName
}
return "${index}_${generatedName}"
}
}

View File

@@ -35,7 +35,8 @@ class RecursiveFilesConverterSpec extends Specification {
private static
final Set<Path> EXPECTED_TARGET_FILES = [Paths.get("dslRoot.json"), Paths.get("dir1/dsl1.json"),
Paths.get("dir1/dsl1b.json"), Paths.get("dir2/dsl2.json"),
Paths.get("dir1/0_dsl1_list.json"), Paths.get("dir1/1_dsl1_list.json")]
Paths.get("dir1/0_dsl1_list.json"), Paths.get("dir1/1_dsl1_list.json"),
Paths.get("dir1/shouldHaveIndex1.json"), Paths.get("dir1/shouldHaveIndex2.json")]
@Rule
public TemporaryFolder tmpFolder = new TemporaryFolder();

View File

@@ -17,10 +17,12 @@
package org.springframework.cloud.contract.verifier.wiremock
import com.github.tomakehurst.wiremock.stubbing.StubMapping
import org.springframework.cloud.contract.verifier.util.ContractVerifierDslConverter
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.verifier.util.ContractVerifierDslConverter
import spock.lang.Specification
import java.util.regex.Pattern
class WireMockToDslConverterSpec extends Specification {
def 'should produce a Groovy DSL from WireMock stub'() {
@@ -69,12 +71,14 @@ class WireMockToDslConverterSpec extends Specification {
}
response {
status 200
body(
id: [value: '132'],
surname: 'Kowalsky',
name: 'Jan',
created: '2014-02-02 12:23:43'
)
body("""{
"id": {
"value": "132"
},
"surname": "Kowalsky",
"name": "Jan",
"created": "2014-02-02 12:23:43"
}""")
headers {
header 'Content-Type': 'text/plain'
@@ -84,10 +88,12 @@ class WireMockToDslConverterSpec extends Specification {
when:
String groovyDsl = WireMockToDslConverter.fromWireMockStub(wireMockStub)
then:
ContractVerifierDslConverter.convert(
def a = ContractVerifierDslConverter.convert(
"""org.springframework.cloud.contract.spec.Contract.make {
$groovyDsl
}""") == expectedGroovyDsl
}""")
def b = expectedGroovyDsl
a == b
}
@@ -97,7 +103,7 @@ class WireMockToDslConverterSpec extends Specification {
{
"request": {
"method": "DELETE",
"urlPattern": "/credit-card-verification-data/[0-9]+",
"urlPattern": "1",
"headers": {
"Content-Type": {
"equalTo": "application/vnd.mymoid-adapter.v2+json; charset=UTF-8"
@@ -119,29 +125,30 @@ class WireMockToDslConverterSpec extends Specification {
Contract expectedGroovyDsl = Contract.make {
request {
method 'DELETE'
url $(consumer(~/\/credit-card-verification-data\/[0-9]+/), producer('/credit-card-verification-data/1'))
url $(consumer(~/1/), producer('1'))
headers {
header('Content-Type': 'application/vnd.mymoid-adapter.v2+json; charset=UTF-8')
}
}
response {
status 200
body("""{
"status": "OK"
body( """{
"status": "OK"
}""")
headers {
header 'Content-Type': 'application/json'
}
}
}
when:
String groovyDsl = WireMockToDslConverter.fromWireMockStub(wireMockStub)
then:
ContractVerifierDslConverter.convert(
def a = ContractVerifierDslConverter.convert(
"""org.springframework.cloud.contract.spec.Contract.make {
$groovyDsl
}""") == expectedGroovyDsl
$groovyDsl
}""")
def b = expectedGroovyDsl
(a.request.url.clientValue as Pattern).pattern() == (b.request.url.clientValue as Pattern).pattern()
}
def 'should convert WireMock stub with response body containing integer'() {
@@ -230,11 +237,14 @@ class WireMockToDslConverterSpec extends Specification {
}
response {
status 200
body([
[a: 1, c: '3'],
'b',
'a'
])
body( """[
{
"a": 1,
"c": "3"
},
"b",
"a"
]""")
headers {
header 'Content-Type': 'application/json'
}
@@ -282,26 +292,26 @@ class WireMockToDslConverterSpec extends Specification {
response {
status 200
body("""[
{
"amount": 1.01,
"name": "Name",
"info": {
"title": "title1",
"payload": null
},
"booleanvalue": true,
"user": null
},
{
"amount": 2.01,
"name": "Name2",
"info": {
"title": "title2",
"payload": null
},
"booleanvalue": true,
"user": null
}
{
"amount": 1.01,
"name": "Name",
"info": {
"title": "title1",
"payload": null
},
"booleanvalue": true,
"user": null
},
{
"amount": 2.01,
"name": "Name2",
"info": {
"title": "title2",
"payload": null
},
"booleanvalue": true,
"user": null
}
]""")
}
}
@@ -337,7 +347,7 @@ class WireMockToDslConverterSpec extends Specification {
request {
method 'POST'
url '/test'
body ('''{"property1":"abc","property2":"2017-01","property3":"666","property4":1428566412}''')
body ('''{"property1":"abc", "property2":"2017-01", "property3":"666", "property4":1428566412}''')
}
response {
status 200
@@ -359,15 +369,15 @@ class WireMockToDslConverterSpec extends Specification {
String wireMockStub = '''\
{
"request": {
"method": "POST",
"url": "/test",
"bodyPatterns": [{
"matches": "[0-9]{5}"
}]
"method": "POST",
"url": "/test",
"bodyPatterns": [{
"matches": "1"
}]
},
"response": {
"status": 200
}
"status": 200
}
}
'''
and:
@@ -377,7 +387,7 @@ class WireMockToDslConverterSpec extends Specification {
request {
method 'POST'
url '/test'
body $(consumer(~/[0-9]{5}/), producer('12345'))
body $(consumer(~/1/), producer('1'))
}
response {
status 200
@@ -391,7 +401,7 @@ class WireMockToDslConverterSpec extends Specification {
$groovyDsl
}""")
and:
evaluatedGroovyDsl == expectedGroovyDsl
(evaluatedGroovyDsl.request.body.clientValue as Pattern).pattern() == (expectedGroovyDsl.request.body.clientValue as Pattern).pattern()
}
def 'should convert WireMock stub with request body with equalToJson'() {
@@ -418,7 +428,7 @@ class WireMockToDslConverterSpec extends Specification {
request {
method 'POST'
url '/test'
body '''{"pan":"4855141150107894","expirationDate":"2017-01","dcvx":"178"}'''
body '''{"pan":"4855141150107894", "expirationDate":"2017-01", "dcvx":"178"}'''
}
response {
status 200
@@ -458,7 +468,7 @@ class WireMockToDslConverterSpec extends Specification {
request {
method 'POST'
url '/test'
body '''{"pan":"4855141150107894","expirationDate":"2017-01","dcvx":"178"}'''
body '''{"pan":"4855141150107894", "expirationDate":"2017-01", "dcvx":"178"}'''
}
response {
status 200
@@ -483,7 +493,7 @@ class WireMockToDslConverterSpec extends Specification {
"url" : "/test",
"method" : "POST",
"bodyPatterns" : [ {
"matches" : "[0-9]{2}"
"matches" : "1"
} ]
},
"response" : {
@@ -498,7 +508,7 @@ class WireMockToDslConverterSpec extends Specification {
request {
method 'POST'
url '/test'
body $(consumer(~/[0-9]{2}/), producer('12'))
body $(consumer(~/1/), producer('1'))
}
response {
status 200
@@ -512,7 +522,7 @@ class WireMockToDslConverterSpec extends Specification {
$groovyDsl
}""")
and:
evaluatedGroovyDsl == expectedGroovyDsl
(evaluatedGroovyDsl.request.body.clientValue as Pattern).pattern() == (expectedGroovyDsl.request.body.clientValue as Pattern).pattern()
}
def 'should convert WireMock stub with priorities'() {

View File

@@ -0,0 +1,33 @@
import org.springframework.cloud.contract.spec.Contract
/*
* Copyright 2013-2016 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(1..2).collect { int index ->
Contract.make {
name("shouldHaveIndex${index}")
request {
method(PUT())
headers {
contentType(applicationJson())
}
url "/${index}"
}
response {
status 200
}
}
}

View File

@@ -113,16 +113,17 @@ class JavaTestGenerator implements SingleTestGenerator {
log.debug("Stub content from file [${stubsFile.text}]")
}
List<Contract> stubContents = metadata.convertedContract
dsls << stubContents.collectEntries { Contract stubContent ->
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
@EqualsAndHashCode(includeFields = true)
private static class ParsedDsl {
ContractMetadata contract
Contract groovyDsl

View File

@@ -57,10 +57,24 @@ class MethodBuilder {
if (log.isDebugEnabled()) {
log.debug("Stub content Groovy DSL [$stubContent]")
}
String methodName = NamesUtil.camelCase(NamesUtil.toLastDot(NamesUtil.afterLast(stubsFile.path, File.separator)))
String methodName = methodName(contract, stubsFile, stubContent)
return new MethodBuilder(methodName, stubContent, configProperties, contract.ignored || stubContent.ignored)
}
private static String methodName(ContractMetadata contract, File stubsFile, Contract stubContent) {
if (stubContent.name) {
return NamesUtil.camelCase(stubContent.name)
} else if (contract.convertedContract.size() > 1) {
int index = contract.convertedContract.findIndexOf { it == stubContent}
return "${camelCasedMethodFromFileName(stubsFile)}_${index}"
}
return camelCasedMethodFromFileName(stubsFile)
}
private static String camelCasedMethodFromFileName(File stubsFile) {
return NamesUtil.camelCase(NamesUtil.toLastDot(NamesUtil.afterLast(stubsFile.path, File.separator)))
}
/**
* Appends to the {@link BlockBuilder} the contents of the test
*/

View File

@@ -17,6 +17,7 @@
package org.springframework.cloud.contract.verifier.file
import groovy.transform.CompileStatic
import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.verifier.util.ContractVerifierDslConverter
@@ -30,6 +31,7 @@ import java.nio.file.Path
* @since 1.0.0
*/
@CompileStatic
@EqualsAndHashCode
@ToString
class ContractMetadata {
/**

View File

@@ -80,7 +80,7 @@ class ContractVerifierDslConverter {
return new GroovyShell(ContractVerifierDslConverter.classLoader, new Binding(), new CompilerConfiguration(sourceEncoding: 'UTF-8'))
}
private static List<Contract> listOfContracts(object) {
private static Collection<Contract> listOfContracts(object) {
if (object instanceof Collection) {
return object as Collection<Contract>
} else if (!object instanceof Contract) {

View File

@@ -106,6 +106,6 @@ class NamesUtil {
* Converts illegal package characters to underscores
*/
static String convertIllegalPackageChars(String packageName) {
return packageName.replace('-', '_')
return packageName.replace('-', '_').replace(" ", "_")
}
}

View File

@@ -38,7 +38,7 @@ class MockMvcMethodBodyBuilderSpec extends Specification implements WireMockStub
Contract dslWithOptionalsInString = Contract.make {
priority 1
request {
method 'POST'
method POST()
url '/users/password'
headers {
contentType(applicationJson())
@@ -65,7 +65,7 @@ class MockMvcMethodBodyBuilderSpec extends Specification implements WireMockStub
Contract dslWithOptionals = Contract.make {
priority 1
request {
method 'POST'
method POST()
url '/users/password'
headers {
contentType(applicationJson())

View File

@@ -26,7 +26,7 @@ import spock.lang.Specification
import static org.springframework.cloud.contract.verifier.config.TestFramework.JUNIT
import static org.springframework.cloud.contract.verifier.config.TestFramework.SPOCK
import static org.springframework.cloud.contract.verifier.util.ContractVerifierDslConverter.convert
import static org.springframework.cloud.contract.verifier.util.ContractVerifierDslConverter.convertAsCollection
class SingleTestGeneratorSpec extends Specification {
@@ -64,7 +64,7 @@ class SingleTestGeneratorSpec extends Specification {
given:
ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties();
properties.targetFramework = testFramework
ContractMetadata contract = new ContractMetadata(file.toPath(), true, 1, 2, convert(file))
ContractMetadata contract = new ContractMetadata(file.toPath(), true, 1, 2, convertAsCollection(file))
contract.ignored >> true
contract.order >> 2
JavaTestGenerator testGenerator = new JavaTestGenerator()
@@ -86,7 +86,7 @@ class SingleTestGeneratorSpec extends Specification {
ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties();
properties.testMode = TestMode.JAXRSCLIENT
properties.targetFramework = testFramework
ContractMetadata contract = new ContractMetadata(file.toPath(), true, 1, 2, convert(file))
ContractMetadata contract = new ContractMetadata(file.toPath(), true, 1, 2, convertAsCollection(file))
contract.ignored >> true
contract.order >> 2
JavaTestGenerator testGenerator = new JavaTestGenerator()
@@ -124,11 +124,11 @@ class SingleTestGeneratorSpec extends Specification {
and:
ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties();
properties.targetFramework = testFramework
ContractMetadata contract = new ContractMetadata(file.toPath(), true, 1, 2, convert(file))
ContractMetadata contract = new ContractMetadata(file.toPath(), true, 1, 2, convertAsCollection(file))
contract.ignored >> true
contract.order >> 2
and:
ContractMetadata contract2 = new ContractMetadata(secondFile.toPath(), true, 1, 2, convert(secondFile))
ContractMetadata contract2 = new ContractMetadata(secondFile.toPath(), true, 1, 2, convertAsCollection(secondFile))
contract2.ignored >> true
contract2.order >> 2
and:
@@ -167,7 +167,7 @@ class SingleTestGeneratorSpec extends Specification {
ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties();
properties.targetFramework = testFramework
and:
ContractMetadata contract2 = new ContractMetadata(secondFile.toPath(), true, 1, 2, convert(file))
ContractMetadata contract2 = new ContractMetadata(secondFile.toPath(), true, 1, 2, convertAsCollection(file))
contract2.ignored >> false
contract2.order >> 2
and:
@@ -186,5 +186,98 @@ class SingleTestGeneratorSpec extends Specification {
SPOCK | spockClassStrings
}
def "should pick the contract's name as the test method"() {
given:
File secondFile = tmpFolder.newFile()
secondFile.write("""
org.springframework.cloud.contract.spec.Contract.make {
name("MySuperMethod")
request {
method 'PUT'
url 'url'
}
response {
status 200
}
}
""")
and:
ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties();
properties.targetFramework = testFramework
ContractMetadata contract = new ContractMetadata(secondFile.toPath(), false, 1, null, convertAsCollection(secondFile))
JavaTestGenerator testGenerator = new JavaTestGenerator()
when:
String clazz = testGenerator.buildClass(properties, [contract], "test", "test", 'com/foo')
then:
clazz.contains("validate_mySuperMethod()")
where:
testFramework << [JUNIT, SPOCK]
}
def "should pick the contract's name as the test method when there are multiple contracts"() {
given:
File secondFile = tmpFolder.newFile()
secondFile.write('''
(1..2).collect { int index ->
org.springframework.cloud.contract.spec.Contract.make {
name("shouldHaveIndex${index}")
request {
method(PUT())
headers {
contentType(applicationJson())
}
url "/${index}"
}
response {
status 200
}
}
}''')
and:
ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties();
properties.targetFramework = testFramework
ContractMetadata contract = new ContractMetadata(secondFile.toPath(), false, 1, null, convertAsCollection(secondFile))
JavaTestGenerator testGenerator = new JavaTestGenerator()
when:
String clazz = testGenerator.buildClass(properties, [contract], "test", "test", 'com/foo')
then:
clazz.contains("validate_shouldHaveIndex1()")
clazz.contains("validate_shouldHaveIndex2()")
where:
testFramework << [JUNIT, SPOCK]
}
def "should generate the test method when there are multiple contracts without name field"() {
given:
File secondFile = tmpFolder.newFile()
secondFile.write('''
(1..2).collect { int index ->
org.springframework.cloud.contract.spec.Contract.make {
request {
method(PUT())
headers {
contentType(applicationJson())
}
url "/${index}"
}
response {
status 200
}
}
}''')
and:
ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties();
properties.targetFramework = testFramework
ContractMetadata contract = new ContractMetadata(secondFile.toPath(), false, 1, null, convertAsCollection(secondFile))
JavaTestGenerator testGenerator = new JavaTestGenerator()
when:
String clazz = testGenerator.buildClass(properties, [contract], "test", "test", 'com/foo')
then:
clazz.contains("_0() throws Exception")
clazz.contains("_1() throws Exception")
where:
testFramework << [JUNIT, SPOCK]
}
}

View File

@@ -0,0 +1,99 @@
package org.springframework.cloud.contract.verifier.util
import spock.lang.Specification
/**
* @author Marcin Grzejszczak
*/
class NamesUtilSpec extends Specification {
def "should return the whole string before the last one"() {
given:
String string = "a.b.c.d.e"
expect:
"a.b.c.d" == NamesUtil.beforeLast(string, ".")
}
def "should return empty string when no token was found for before last"() {
given:
String string = "a.b.c.d.e"
expect:
"" == NamesUtil.beforeLast(string, "/")
}
def "should return first token after the last one"() {
given:
String string = "a.b.c.d.e"
expect:
"e" == NamesUtil.afterLast(string, ".")
}
def "should return the input string when no token was found for after last"() {
given:
String string = "a.b.c.d.e"
expect:
string == NamesUtil.afterLast(string, "/")
}
def "should return first token after the last dot"() {
given:
String string = "a.b.c.d.e"
expect:
"e" == NamesUtil.afterLastDot(string)
}
def "should return the input string when no token was found for after last dot"() {
given:
String string = "abcde"
expect:
string == NamesUtil.afterLastDot(string)
}
def "should return camel case version of a string"() {
given:
String string = "BlaBlaBla"
expect:
"blaBlaBla" == NamesUtil.camelCase(string)
}
def "should return capitalized version of a string"() {
given:
String string = "blaBlaBla"
expect:
"BlaBlaBla" == NamesUtil.capitalize(string)
}
def "should return all text to last dot"() {
given:
String string = "a.b.c.d.e"
expect:
"a.b.c.d" == NamesUtil.toLastDot(string)
}
def "should return the input string when no token was found for to last dot"() {
given:
String string = "abcde"
expect:
string == NamesUtil.toLastDot(string)
}
def "should convert a package notation to directory"() {
given:
String string = "a.b.c.d.e"
expect:
"a/b/c/d/e".replace("/", File.separator) == NamesUtil.packageToDirectory(string)
}
def "should convert a directory notation to package"() {
given:
String string = "a/b/c/d/e".replace("/", File.separator)
expect:
"a.b.c.d.e" == NamesUtil.directoryToPackage(string)
}
def "should convert all illegal package chars to legal ones"() {
given:
String string = "a-b c"
expect:
"a_b_c" == NamesUtil.convertIllegalPackageChars(string)
}
}