Fixed the contract to yml conversion; fixes gh-732

This commit is contained in:
Marcin Grzejszczak
2018-09-17 17:52:01 +02:00
parent 7b75c0345d
commit 52a5ed6cec
5 changed files with 868 additions and 48 deletions

View File

@@ -16,11 +16,13 @@
package org.springframework.cloud.contract.spec.internal
import java.util.regex.Pattern
import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString
import groovy.transform.TypeChecked
import java.util.regex.Pattern
import groovy.transform.stc.ClosureParams
import groovy.transform.stc.SimpleType
/**
* Represents a set of headers of a request / response or a message
@@ -80,27 +82,34 @@ class Headers {
}
/**
* Converts the headers into their stub side representations and returns as
* a map of String key => Object value.
* Converts the headers via the provided closure
*/
Map<String , Object> asStubSideMap() {
Map<String , Object> asMap(@ClosureParams(value = SimpleType.class,
options = [
"java.lang.String",
"org.springframework.cloud.contract.spec.internal.DslProperty"
]) Closure closure) {
def acc = [:].withDefault { [] as Collection<Object> }
return entries.inject(acc as Map<String, Object>) { Map<String, Object> map, Header header ->
map[header.name] = header.clientValue
map[header.name] = closure(header.name, header)
return map
} as Map<String , Object>
}
/**
* Converts the headers into their stub side representations and returns as
* a map of String key => Object value.
*/
Map<String , Object> asStubSideMap() {
return asMap({ String headerName, DslProperty prop -> prop.clientValue })
}
/**
* Converts the headers into their stub side representations and returns as
* a map of String key => Object value.
*/
Map<String , Object> asTestSideMap() {
def acc = [:].withDefault { [] as Collection<Object> }
return entries.inject(acc as Map<String, Object>) { Map<String, Object> map, Header header ->
map[header.name] = header.serverValue
return map
} as Map<String , Object>
return asMap({ String headerName, DslProperty prop -> prop.serverValue })
}
boolean equals(o) {

View File

@@ -17,6 +17,8 @@
package org.springframework.cloud.contract.verifier.converter
import groovy.transform.CompileStatic
import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString
/**
* YAML representation of a {@link org.springframework.cloud.contract.spec.Contract}
@@ -38,6 +40,8 @@ class YamlContract {
public boolean ignored
@CompileStatic
@ToString(includeFields = true)
@EqualsAndHashCode
static class Request {
public String method
public String url
@@ -52,12 +56,16 @@ class YamlContract {
}
@CompileStatic
@ToString(includeFields = true)
@EqualsAndHashCode
static class Multipart {
public Map<String, String> params = [:]
public List<Named> named = []
}
@CompileStatic
@ToString(includeFields = true)
@EqualsAndHashCode
static class Named {
public String paramName
public String fileName
@@ -65,7 +73,10 @@ class YamlContract {
}
@CompileStatic
@ToString(includeFields = true)
@EqualsAndHashCode
static class StubMatchers {
// TODO: Add URL matching
public List<BodyStubMatcher> body = []
public List<KeyValueMatcher> headers = []
public List<KeyValueMatcher> cookies = []
@@ -73,6 +84,8 @@ class YamlContract {
}
@CompileStatic
@ToString(includeFields = true)
@EqualsAndHashCode
static class BodyStubMatcher {
public String path
public StubMatcherType type
@@ -81,12 +94,16 @@ class YamlContract {
}
@CompileStatic
@ToString(includeFields = true)
@EqualsAndHashCode
static class MultipartStubMatcher {
public List<KeyValueMatcher> params = []
public List<MultipartNamedStubMatcher> named = []
}
@CompileStatic
@ToString(includeFields = true)
@EqualsAndHashCode
static class MultipartNamedStubMatcher {
public String paramName
public ValueMatcher fileName
@@ -94,12 +111,16 @@ class YamlContract {
}
@CompileStatic
@ToString(includeFields = true)
@EqualsAndHashCode
static class ValueMatcher {
public String regex
public PredefinedRegex predefined
}
@CompileStatic
@ToString(includeFields = true)
@EqualsAndHashCode
static class BodyTestMatcher {
public String path
public TestMatcherType type
@@ -110,6 +131,8 @@ class YamlContract {
}
@CompileStatic
@ToString(includeFields = true)
@EqualsAndHashCode
static class KeyValueMatcher {
public String key
public String regex
@@ -117,6 +140,8 @@ class YamlContract {
}
@CompileStatic
@ToString(includeFields = true)
@EqualsAndHashCode
static class TestHeaderMatcher {
public String key
public String regex
@@ -125,6 +150,8 @@ class YamlContract {
}
@CompileStatic
@ToString(includeFields = true)
@EqualsAndHashCode
static class TestCookieMatcher {
public String key
public String regex
@@ -150,6 +177,8 @@ class YamlContract {
}
@CompileStatic
@ToString(includeFields = true)
@EqualsAndHashCode
static class Response {
public int status
public Map<String, Object> headers = [:]
@@ -161,6 +190,8 @@ class YamlContract {
}
@CompileStatic
@ToString(includeFields = true)
@EqualsAndHashCode
static class TestMatchers {
public List<BodyTestMatcher> body = []
public List<TestHeaderMatcher> headers = []
@@ -168,6 +199,8 @@ class YamlContract {
}
@CompileStatic
@ToString(includeFields = true)
@EqualsAndHashCode
static class Input {
public String messageFrom
public String triggeredBy
@@ -179,6 +212,8 @@ class YamlContract {
}
@CompileStatic
@ToString(includeFields = true)
@EqualsAndHashCode
static class OutputMessage {
public String sentTo
public Map<String, Object> headers = [:]

View File

@@ -503,31 +503,34 @@ class YamlContractConverter implements ContractConverter<List<YamlContract>> {
List<YamlContract> convertTo(Collection<Contract> contracts) {
return contracts.collect { Contract contract ->
YamlContract yamlContract = new YamlContract()
yamlContract.description = contract.description
if (contract?.request?.method) {
yamlContract.request = new Request()
yamlContract.request.with {
method = contract?.request?.method?.clientValue
url = contract?.request?.url?.clientValue
headers = (contract?.request?.headers as Headers)?.asTestSideMap()
cookies = (contract?.request?.cookies as Cookies)?.asTestSideMap()
body = MapConverter.getTestSideValues(contract?.request?.body)
matchers = new StubMatchers()
yamlContract.request.with { Request request ->
request.method = contract?.request?.method?.clientValue
request.url = contract?.request?.url?.clientValue
request.urlPath = contract?.request?.urlPath?.clientValue
request.headers = (contract?.request?.headers as Headers)?.asTestSideMap()
request.cookies = (contract?.request?.cookies as Cookies)?.asTestSideMap()
request.body = MapConverter.getTestSideValues(contract?.request?.body)
request.matchers = new StubMatchers()
contract?.request?.bodyMatchers?.jsonPathMatchers()?.each { BodyMatcher matcher ->
matchers.body << new BodyStubMatcher(
request.matchers.body << new BodyStubMatcher(
path: matcher.path(),
type: stubMatcherType(matcher.matchingType()),
value: matcher.value()?.toString()
)
}
setInputHeaders(contract?.request?.headers as Headers, yamlContract.request.matchers.headers)
}
yamlContract.response = new Response()
yamlContract.response.with {
status = contract?.response?.status?.clientValue as Integer
headers = (contract?.response?.headers as Headers)?.asStubSideMap()
cookies = (contract?.response?.cookies as Cookies)?.asStubSideMap()
body = MapConverter.getStubSideValues(contract?.response?.body)
yamlContract.response.with { Response response ->
response.status = contract?.response?.status?.clientValue as Integer
response.headers = (contract?.response?.headers as Headers)?.asStubSideMap()
response.cookies = (contract?.response?.cookies as Cookies)?.asStubSideMap()
response.body = MapConverter.getStubSideValues(contract?.response?.body)
contract?.response?.bodyMatchers?.jsonPathMatchers()?.each { BodyMatcher matcher ->
matchers.body << new BodyTestMatcher(
response.matchers.body << new BodyTestMatcher(
path: matcher.path(),
type: testMatcherType(matcher.matchingType()),
value: matcher.value()?.toString(),
@@ -535,45 +538,73 @@ class YamlContractConverter implements ContractConverter<List<YamlContract>> {
maxOccurrence: matcher.maxTypeOccurrence()
)
}
setOutputHeaders(contract?.response?.headers, yamlContract.response.matchers.headers)
}
}
yamlContract.label = contract.label
if (contract.input) {
yamlContract.input = new Input()
yamlContract.input.assertThat = contract?.input?.assertThat?.toString()
yamlContract.input.triggeredBy = contract?.input?.triggeredBy?.toString()
yamlContract.input.assertThat = MapConverter.getTestSideValues(contract?.input?.assertThat?.toString())
yamlContract.input.triggeredBy = MapConverter.getTestSideValues(contract?.input?.triggeredBy?.toString())
yamlContract.input.messageHeaders = (contract?.input?.messageHeaders as Headers)?.asTestSideMap()
yamlContract.input.messageBody = MapConverter.getTestSideValues(contract?.input?.messageBody)
yamlContract.input.messageFrom = contract?.input?.messageFrom?.serverValue
yamlContract.input.matchers.body.each {
contract?.input?.bodyMatchers?.jsonPathMatchers()?.each { BodyMatcher matcher ->
yamlContract.input.matchers.body << new BodyStubMatcher(
path: matcher.path(),
type: stubMatcherType(matcher.matchingType()),
value: matcher.value()?.toString()
)
}
yamlContract.input.messageFrom = MapConverter.getTestSideValues(contract?.input?.messageFrom)
contract?.input?.bodyMatchers?.jsonPathMatchers()?.each { BodyMatcher matcher ->
yamlContract.input.matchers.body << new BodyStubMatcher(
path: matcher.path(),
type: stubMatcherType(matcher.matchingType()),
value: matcher.value()?.toString()
)
}
setInputHeaders(contract?.input?.messageHeaders as Headers, yamlContract.input.matchers.headers)
}
if (contract.outputMessage) {
yamlContract.outputMessage = new OutputMessage()
yamlContract.outputMessage.sentTo = MapConverter.getStubSideValues(contract.outputMessage.sentTo)
yamlContract.outputMessage.headers = (contract?.outputMessage?.headers as Headers)?.asStubSideMap()
yamlContract.outputMessage.body = MapConverter.getStubSideValues(contract?.outputMessage?.body)
yamlContract.outputMessage.matchers.body.each {
contract?.input?.bodyMatchers?.jsonPathMatchers()?.each { BodyMatcher matcher ->
yamlContract.outputMessage.matchers.body << new BodyTestMatcher(
path: matcher.path(),
type: testMatcherType(matcher.matchingType()),
value: matcher.value()?.toString(),
minOccurrence: matcher.minTypeOccurrence(),
maxOccurrence: matcher.maxTypeOccurrence()
)
}
contract?.outputMessage?.bodyMatchers?.jsonPathMatchers()?.each { BodyMatcher matcher ->
yamlContract.outputMessage.matchers.body << new BodyTestMatcher(
path: matcher.path(),
type: testMatcherType(matcher.matchingType()),
value: matcher.value()?.toString(),
minOccurrence: matcher.minTypeOccurrence(),
maxOccurrence: matcher.maxTypeOccurrence()
)
}
setOutputHeaders(contract?.outputMessage?.headers, yamlContract.outputMessage.matchers.headers)
}
return yamlContract
}
}
protected void setInputHeaders(Headers headers, List<KeyValueMatcher> headerMatchers) {
headers?.asStubSideMap()?.each { String key, Object value ->
if (value instanceof Pattern) {
headerMatchers << new KeyValueMatcher(
key: key,
regex: value.pattern(),
)
}
}
}
protected void setOutputHeaders(Headers headers, List<TestHeaderMatcher> headerMatchers) {
headers?.asTestSideMap()?.each { String key, Object value ->
if (value instanceof Pattern) {
headerMatchers << new TestHeaderMatcher(
key: key,
regex: value.pattern(),
)
} else if (value instanceof ExecutionProperty) {
headerMatchers << new TestHeaderMatcher(
key: key,
command: value.executionCommand,
)
}
}
}
protected TestMatcherType testMatcherType(MatchingType matchingType) {
switch (matchingType) {
case MatchingType.EQUALITY:

View File

@@ -0,0 +1,328 @@
/*
* 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
*
* 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.
*/
package org.springframework.cloud.contract.verifier.converter
import spock.lang.Specification
import org.springframework.cloud.contract.spec.Contract
/**
* @author Marcin Grzejszczak
* @author Tim Ysewyn
*/
class DslToYamlContractConverterSpec extends Specification {
YamlContractConverter converter = new YamlContractConverter()
def "should convert rest DSL to YAML"() {
given:
List<Contract> contracts = [Contract.make {
request {
method 'GET'
urlPath '/get'
body([
duck: 123,
alpha: "abc",
number: 123,
aBoolean: true,
date: "2017-01-01",
dateTime: "2017-01-01T01:23:45",
time: "01:02:34",
valueWithoutAMatcher: "foo",
valueWithTypeMatch: "string",
key: [
'complex.key' : 'foo'
]
])
bodyMatchers {
jsonPath('$.duck', byRegex("[0-9]{3}"))
jsonPath('$.duck', byEquality())
jsonPath('$.alpha', byRegex(onlyAlphaUnicode()))
jsonPath('$.alpha', byEquality())
jsonPath('$.number', byRegex(number()))
jsonPath('$.aBoolean', byRegex(anyBoolean()))
jsonPath('$.date', byDate())
jsonPath('$.dateTime', byTimestamp())
jsonPath('$.time', byTime())
jsonPath("\$.['key'].['complex.key']", byEquality())
}
headers {
header("sample", $(c(regex('foo.*')), p('foo')))
header("Content-Type", applicationJson())
}
}
response {
status OK()
body([
duck: 123,
alpha: "abc",
number: 123,
positiveInteger: 1234567890,
negativeInteger: -1234567890,
positiveDecimalNumber: 123.4567890,
negativeDecimalNumber: -123.4567890,
aBoolean: true,
date: "2017-01-01",
dateTime: "2017-01-01T01:23:45",
time: "01:02:34",
valueWithoutAMatcher: "foo",
valueWithTypeMatch: "string",
valueWithMin: [
1,2,3
],
valueWithMax: [
1,2,3
],
valueWithMinMax: [
1,2,3
],
valueWithMinEmpty: [],
valueWithMaxEmpty: [],
key: [
'complex.key' : 'foo'
],
nullValue: null
])
bodyMatchers {
// asserts the jsonpath value against manual regex
jsonPath('$.duck', byRegex("[0-9]{3}"))
// asserts the jsonpath value against the provided value
jsonPath('$.duck', byEquality())
// asserts the jsonpath value against some default regex
jsonPath('$.alpha', byRegex(onlyAlphaUnicode()))
jsonPath('$.alpha', byEquality())
jsonPath('$.number', byRegex(number()))
jsonPath('$.positiveInteger', byRegex(positiveInt()))
jsonPath('$.integer', byRegex(anInteger()))
jsonPath('$.double', byRegex(aDouble()))
jsonPath('$.aBoolean', byRegex(anyBoolean()))
// asserts vs inbuilt time related regex
jsonPath('$.date', byDate())
jsonPath('$.dateTime', byTimestamp())
jsonPath('$.time', byTime())
// asserts that the resulting type is the same as in response body
jsonPath('$.valueWithTypeMatch', byType())
jsonPath('$.valueWithMin', byType {
// results in verification of size of array (min 1)
minOccurrence(1)
})
jsonPath('$.valueWithMax', byType {
// results in verification of size of array (max 3)
maxOccurrence(3)
})
jsonPath('$.valueWithMinMax', byType {
// results in verification of size of array (min 1 & max 3)
minOccurrence(1)
maxOccurrence(3)
})
jsonPath('$.valueWithMinEmpty', byType {
// results in verification of size of array (min 0)
minOccurrence(0)
})
jsonPath('$.valueWithMaxEmpty', byType {
// results in verification of size of array (max 0)
maxOccurrence(0)
})
// will execute a method `assertThatValueIsANumber`
jsonPath('$.duck', byCommand('assertThatValueIsANumber($it)'))
jsonPath("\$.['key'].['complex.key']", byEquality())
jsonPath('$.nullValue', byNull())
}
headers {
contentType(applicationJson())
header('Some-Header', $(c('someValue'), p(regex('[a-zA-Z]{9}'))))
}
}
}]
when:
Collection<YamlContract> yamlContracts = converter.convertTo(contracts)
then:
yamlContracts.size() == 1
YamlContract yamlContract = yamlContracts.first()
yamlContract.request.method == "GET"
yamlContract.request.urlPath == "/get"
yamlContract.request.body == [
duck: 123,
alpha: "abc",
number: 123,
aBoolean: true,
date: "2017-01-01",
dateTime: "2017-01-01T01:23:45",
time: "01:02:34",
valueWithoutAMatcher: "foo",
valueWithTypeMatch: "string",
key: ["complex.key": 'foo']
]
yamlContract.request.headers == [
sample: 'foo',
"Content-Type": "application/json"
]
yamlContract.request.matchers.headers == [
new YamlContract.KeyValueMatcher(
key: "sample", regex: "foo.*")
]
yamlContract.request.matchers.body == [
new YamlContract.BodyStubMatcher(
path: '$.duck',
type: YamlContract.StubMatcherType.by_regex,
value: "[0-9]{3}"),
new YamlContract.BodyStubMatcher(
path: '$.duck',
type: YamlContract.StubMatcherType.by_equality),
new YamlContract.BodyStubMatcher(
path: '$.alpha',
type: YamlContract.StubMatcherType.by_regex,
predefined: YamlContract.PredefinedRegex.only_alpha_unicode),
new YamlContract.BodyStubMatcher(
path: '$.alpha',
type: YamlContract.StubMatcherType.by_equality),
new YamlContract.BodyStubMatcher(
path: '$.number',
type: YamlContract.StubMatcherType.by_regex,
predefined: YamlContract.PredefinedRegex.number),
new YamlContract.BodyStubMatcher(
path: '$.aBoolean',
type: YamlContract.StubMatcherType.by_regex,
predefined: YamlContract.PredefinedRegex.any_boolean),
new YamlContract.BodyStubMatcher(
path: '$.date',
type: YamlContract.StubMatcherType.by_date),
new YamlContract.BodyStubMatcher(
path: '$.dateTime',
type: YamlContract.StubMatcherType.by_timestamp),
new YamlContract.BodyStubMatcher(
path: '$.time',
type: YamlContract.StubMatcherType.by_time),
new YamlContract.BodyStubMatcher(
path: "\$.['key'].['complex.key']",
type: YamlContract.StubMatcherType.by_equality),
]
yamlContract.response.status == 200
yamlContract.response.body == [duck: 123,
alpha: "abc",
number: 123,
aBoolean: true,
date: "2017-01-01",
dateTime: "2017-01-01T01:23:45",
time: "01:02:34",
positiveInteger: 1234567890,
negativeInteger: -1234567890,
positiveDecimalNumber: 123.4567890,
negativeDecimalNumber: -123.4567890,
valueWithoutAMatcher: "foo",
valueWithTypeMatch: "string",
valueWithMin: [1, 2, 3],
valueWithMax: [1, 2, 3],
valueWithMinMax: [1, 2, 3],
valueWithMinEmpty: [],
valueWithMaxEmpty: [],
key: ['complex.key' : 'foo'],
nullValue: null
]
yamlContract.response.headers == [
"Content-Type": "application/json",
"Some-Header": "someValue"
]
yamlContract.response.matchers.headers == [
new YamlContract.TestHeaderMatcher(
key: "Content-Type", regex: "application/json.*")
]
yamlContract.response.matchers.body == [
new YamlContract.BodyTestMatcher(
path: '$.duck',
type: YamlContract.TestMatcherType.by_regex,
value: "[0-9]{3}"),
new YamlContract.BodyTestMatcher(
path: '$.duck',
type: YamlContract.TestMatcherType.by_equality),
new YamlContract.BodyTestMatcher(
path: '$.alpha',
type: YamlContract.TestMatcherType.by_regex,
value: '[\\p{L}]*'),
new YamlContract.BodyTestMatcher(
path: '$.alpha',
type: YamlContract.TestMatcherType.by_equality),
new YamlContract.BodyTestMatcher(
path: '$.number',
type: YamlContract.TestMatcherType.by_regex,
value: '-?(\\d*\\.\\d+|\\d+)'),
new YamlContract.BodyTestMatcher(
path: '$.positiveInteger',
type: YamlContract.TestMatcherType.by_regex,
value: '([1-9]\\d*)'),
new YamlContract.BodyTestMatcher(
path: '$.integer',
type: YamlContract.TestMatcherType.by_regex,
value: '-?(\\d+)'),
new YamlContract.BodyTestMatcher(
path: '$.double',
type: YamlContract.TestMatcherType.by_regex,
value: '-?(\\d*\\.\\d+)'),
new YamlContract.BodyTestMatcher(
path: '$.aBoolean',
type: YamlContract.TestMatcherType.by_regex,
value: '(true|false)'),
new YamlContract.BodyTestMatcher(
path: '$.date',
type: YamlContract.TestMatcherType.by_regex,
value: '(\\d\\d\\d\\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])'),
new YamlContract.BodyTestMatcher(
path: '$.dateTime',
type: YamlContract.TestMatcherType.by_regex,
value: '([0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])'),
new YamlContract.BodyTestMatcher(
path: '$.time',
type: YamlContract.TestMatcherType.by_regex,
value: '(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])'),
new YamlContract.BodyTestMatcher(
path: '$.valueWithTypeMatch',
type: YamlContract.TestMatcherType.by_type),
new YamlContract.BodyTestMatcher(
path: '$.valueWithMin',
type: YamlContract.TestMatcherType.by_type,
minOccurrence: 1),
new YamlContract.BodyTestMatcher(
path: '$.valueWithMax',
type: YamlContract.TestMatcherType.by_type,
maxOccurrence: 3),
new YamlContract.BodyTestMatcher(
path: '$.valueWithMinMax',
type: YamlContract.TestMatcherType.by_type,
minOccurrence: 1,
maxOccurrence: 3),
new YamlContract.BodyTestMatcher(
path: '$.valueWithMinEmpty',
type: YamlContract.TestMatcherType.by_type,
minOccurrence: 0),
new YamlContract.BodyTestMatcher(
path: '$.valueWithMaxEmpty',
type: YamlContract.TestMatcherType.by_type,
maxOccurrence: 0),
new YamlContract.BodyTestMatcher(
path: '$.duck',
type: YamlContract.TestMatcherType.by_command,
value: 'assertThatValueIsANumber($it)'),
new YamlContract.BodyTestMatcher(
path: "\$.['key'].['complex.key']",
type: YamlContract.TestMatcherType.by_equality),
new YamlContract.BodyTestMatcher(
path: '$.nullValue',
type: YamlContract.TestMatcherType.by_null),
]
}
}

View File

@@ -519,7 +519,7 @@ class YamlContractConverterSpec extends Specification {
}
}
def "should convert DSL to YAML"() {
def "should convert HTTP DSL to YAML"() {
given:
assert converter.isAccepted(ymlWithRest)
and:
@@ -565,4 +565,421 @@ class YamlContractConverterSpec extends Specification {
yamlContract.response.cookies.find { it.key == "foo" && it.value == "client" }
yamlContract.response.cookies.find { it.key == "bar" && it.value == "client" }
}
}
def "should convert multiple messaging DSLs to YAML"() {
given:
assert converter.isAccepted(ymlMessagingMethod)
and:
List<Contract> contracts = [Contract.make {
input {
description("Some description")
label("some_label")
triggeredBy("bookReturnedTriggered()")
}
outputMessage {
sentTo("output")
body([bookName: "foo"])
headers {
header("BOOK-NAME", "foo")
}
}
},Contract.make {
input {
description("Some description2")
label("some_label2")
triggeredBy("bookReturnedTriggered()2")
}
outputMessage {
sentTo("output2")
body([bookName2: "foo"])
headers {
header("BOOK-NAME2", "foo")
}
}
}]
when:
Collection<YamlContract> yamlContracts = converter.convertTo(contracts)
then:
yamlContracts.size() == 2
YamlContract yamlContract = yamlContracts.first()
yamlContract.description == "Some description"
yamlContract.label == "some_label"
yamlContract.input.triggeredBy == "bookReturnedTriggered()"
yamlContract.outputMessage.sentTo == "output"
yamlContract.outputMessage.body == [bookName: "foo"]
yamlContract.outputMessage.headers == ["BOOK-NAME": "foo"]
and:
YamlContract yamlContract2 = yamlContracts.last()
yamlContract2.description == "Some description2"
yamlContract2.label == "some_label2"
yamlContract2.input.triggeredBy == "bookReturnedTriggered()2"
yamlContract2.outputMessage.sentTo == "output2"
yamlContract2.outputMessage.body == [bookName2: "foo"]
yamlContract2.outputMessage.headers == ["BOOK-NAME2": "foo"]
}
def "should convert Messaging DSL with input triggered by method to YAML"() {
given:
assert converter.isAccepted(ymlMessagingMethod)
and:
List<Contract> contracts = [Contract.make {
input {
description("Some description")
label("some_label")
triggeredBy("bookReturnedTriggered()")
}
outputMessage {
sentTo("output")
body([bookName: "foo"])
headers {
header("BOOK-NAME", "foo")
}
}
}]
when:
Collection<YamlContract> yamlContracts = converter.convertTo(contracts)
then:
yamlContracts.size() == 1
YamlContract yamlContract = yamlContracts.first()
yamlContract.description == "Some description"
yamlContract.label == "some_label"
yamlContract.input.triggeredBy == "bookReturnedTriggered()"
yamlContract.outputMessage.sentTo == "output"
yamlContract.outputMessage.body == [bookName: "foo"]
yamlContract.outputMessage.headers == ["BOOK-NAME": "foo"]
}
def "should convert Messaging DSL with input and output message to YAML"() {
given:
List<Contract> contracts = [Contract.make {
input {
messageFrom("jms:input")
messageBody([bookName: 'foo'])
messageHeaders {
header("sample", "header")
}
}
outputMessage {
sentTo("output")
body([bookName: "foo"])
headers {
header("BOOK-NAME", "foo")
}
}
}]
when:
Collection<YamlContract> yamlContracts = converter.convertTo(contracts)
then:
yamlContracts.size() == 1
YamlContract yamlContract = yamlContracts.first()
yamlContract.input.messageFrom == "jms:input"
yamlContract.input.messageBody == [bookName: 'foo']
yamlContract.input.messageHeaders == ["sample": "header"]
yamlContract.outputMessage.sentTo == "output"
yamlContract.outputMessage.body == [bookName: "foo"]
yamlContract.outputMessage.headers == ["BOOK-NAME": "foo"]
}
def "should convert Messaging DSL with only input message to YAML"() {
given:
List<Contract> contracts = [Contract.make {
input {
messageFrom("jms:input")
messageBody([bookName: 'foo'])
messageHeaders {
header("sample", "header")
}
assertThat("bookWasDeleted()")
}
}]
when:
Collection<YamlContract> yamlContracts = converter.convertTo(contracts)
then:
yamlContracts.size() == 1
YamlContract yamlContract = yamlContracts.first()
yamlContract.input.messageFrom == "jms:input"
yamlContract.input.messageBody == [bookName: 'foo']
yamlContract.input.messageHeaders == ["sample": "header"]
yamlContract.input.assertThat == "bookWasDeleted()"
}
def "should convert Messaging with a message DSL to YAML"() {
given:
assert converter.isAccepted(ymlMessagingMatchers)
and:
List<Contract> contracts = [Contract.make {
label("card_rejected")
input {
messageFrom("input")
messageBody([
duck: 123,
alpha: "abc",
number: 123,
aBoolean: true,
date: "2017-01-01",
dateTime: "2017-01-01T01:23:45",
time: "01:02:34",
valueWithoutAMatcher: "foo",
valueWithTypeMatch: "string",
key: ["complex.key": 'foo']
])
bodyMatchers {
jsonPath('$.duck', byRegex("[0-9]{3}"))
jsonPath('$.duck', byEquality())
jsonPath('$.alpha', byRegex(onlyAlphaUnicode()))
jsonPath('$.alpha', byEquality())
jsonPath('$.number', byRegex(number()))
jsonPath('$.aBoolean', byRegex(anyBoolean()))
jsonPath('$.date', byDate())
jsonPath('$.dateTime', byTimestamp())
jsonPath('$.time', byTime())
jsonPath("\$.['key'].['complex.key']", byEquality())
}
messageHeaders {
header("sample", $(c(regex("foo.*")), p("foo")))
messagingContentType(applicationJson())
}
}
outputMessage {
sentTo("channel")
body([duck: 123,
alpha: "abc",
number: 123,
aBoolean: true,
date: "2017-01-01",
dateTime: "2017-01-01T01:23:45",
time: "01:02:34",
valueWithoutAMatcher: "foo",
valueWithTypeMatch: "string",
valueWithMin: [1, 2, 3],
valueWithMax: [1, 2, 3],
valueWithMinMax: [1, 2, 3],
valueWithMinEmpty: [],
valueWithMaxEmpty: [],
key: ['complex.key' : 'foo'],
nullValue: null
])
bodyMatchers {
// asserts the jsonpath value against manual regex
jsonPath('$.duck', byRegex("[0-9]{3}"))
// asserts the jsonpath value against the provided value
jsonPath('$.duck', byEquality())
// asserts the jsonpath value against some default regex
jsonPath('$.alpha', byRegex(onlyAlphaUnicode()))
jsonPath('$.alpha', byEquality())
jsonPath('$.number', byRegex(number()))
jsonPath('$.positiveInteger', byRegex(positiveInt()))
jsonPath('$.integer', byRegex(anInteger()))
jsonPath('$.double', byRegex(aDouble()))
jsonPath('$.aBoolean', byRegex(anyBoolean()))
// asserts vs inbuilt time related regex
jsonPath('$.date', byDate())
jsonPath('$.dateTime', byTimestamp())
jsonPath('$.time', byTime())
// asserts that the resulting type is the same as in response body
jsonPath('$.valueWithTypeMatch', byType())
jsonPath('$.valueWithMin', byType {
// results in verification of size of array (min 1)
minOccurrence(1)
})
jsonPath('$.valueWithMax', byType {
// results in verification of size of array (max 3)
maxOccurrence(3)
})
jsonPath('$.valueWithMinMax', byType {
// results in verification of size of array (min 1 & max 3)
minOccurrence(1)
maxOccurrence(3)
})
jsonPath('$.valueWithMinEmpty', byType {
// results in verification of size of array (min 0)
minOccurrence(0)
})
jsonPath('$.valueWithMaxEmpty', byType {
// results in verification of size of array (max 0)
maxOccurrence(0)
})
// will execute a method `assertThatValueIsANumber`
jsonPath('$.duck', byCommand('assertThatValueIsANumber($it)'))
jsonPath("\$.['key'].['complex.key']", byEquality())
jsonPath('$.nullValue', byNull())
}
headers {
messagingContentType(applicationJson())
header('Some-Header', $(c('someValue'), p(regex('[a-zA-Z]{9}'))))
}
}
}]
when:
Collection<YamlContract> yamlContracts = converter.convertTo(contracts)
then:
yamlContracts.size() == 1
YamlContract yamlContract = yamlContracts.first()
yamlContract.label == "card_rejected"
yamlContract.input.messageFrom == "input"
yamlContract.input.messageBody == [
duck: 123,
alpha: "abc",
number: 123,
aBoolean: true,
date: "2017-01-01",
dateTime: "2017-01-01T01:23:45",
time: "01:02:34",
valueWithoutAMatcher: "foo",
valueWithTypeMatch: "string",
key: ["complex.key": 'foo']
]
yamlContract.input.messageHeaders == [
sample: 'foo',
contentType: "application/json"
]
yamlContract.input.matchers.headers == [
new YamlContract.KeyValueMatcher(
key: "sample", regex: "foo.*")
]
yamlContract.input.matchers.body == [
new YamlContract.BodyStubMatcher(
path: '$.duck',
type: YamlContract.StubMatcherType.by_regex,
value: "[0-9]{3}"),
new YamlContract.BodyStubMatcher(
path: '$.duck',
type: YamlContract.StubMatcherType.by_equality),
new YamlContract.BodyStubMatcher(
path: '$.alpha',
type: YamlContract.StubMatcherType.by_regex,
predefined: YamlContract.PredefinedRegex.only_alpha_unicode),
new YamlContract.BodyStubMatcher(
path: '$.alpha',
type: YamlContract.StubMatcherType.by_equality),
new YamlContract.BodyStubMatcher(
path: '$.number',
type: YamlContract.StubMatcherType.by_regex,
predefined: YamlContract.PredefinedRegex.number),
new YamlContract.BodyStubMatcher(
path: '$.aBoolean',
type: YamlContract.StubMatcherType.by_regex,
predefined: YamlContract.PredefinedRegex.any_boolean),
new YamlContract.BodyStubMatcher(
path: '$.date',
type: YamlContract.StubMatcherType.by_date),
new YamlContract.BodyStubMatcher(
path: '$.dateTime',
type: YamlContract.StubMatcherType.by_timestamp),
new YamlContract.BodyStubMatcher(
path: '$.time',
type: YamlContract.StubMatcherType.by_time),
new YamlContract.BodyStubMatcher(
path: "\$.['key'].['complex.key']",
type: YamlContract.StubMatcherType.by_equality),
]
yamlContract.outputMessage.sentTo == "channel"
yamlContract.outputMessage.body == [duck: 123,
alpha: "abc",
number: 123,
aBoolean: true,
date: "2017-01-01",
dateTime: "2017-01-01T01:23:45",
time: "01:02:34",
valueWithoutAMatcher: "foo",
valueWithTypeMatch: "string",
valueWithMin: [1, 2, 3],
valueWithMax: [1, 2, 3],
valueWithMinMax: [1, 2, 3],
valueWithMinEmpty: [],
valueWithMaxEmpty: [],
key: ['complex.key' : 'foo'],
nullValue: null
]
yamlContract.outputMessage.headers == [
"contentType": "application/json",
"Some-Header": "someValue"
]
yamlContract.outputMessage.matchers.headers == [
new YamlContract.TestHeaderMatcher(
key: "Content-Type", regex: "application/json.*")
]
yamlContract.outputMessage.matchers.body == [
new YamlContract.BodyTestMatcher(
path: '$.duck',
type: YamlContract.TestMatcherType.by_regex,
value: "[0-9]{3}"),
new YamlContract.BodyTestMatcher(
path: '$.duck',
type: YamlContract.TestMatcherType.by_equality),
new YamlContract.BodyTestMatcher(
path: '$.alpha',
type: YamlContract.TestMatcherType.by_regex,
value: '[\\p{L}]*'),
new YamlContract.BodyTestMatcher(
path: '$.alpha',
type: YamlContract.TestMatcherType.by_equality),
new YamlContract.BodyTestMatcher(
path: '$.number',
type: YamlContract.TestMatcherType.by_regex,
value: '-?(\\d*\\.\\d+|\\d+)'),
new YamlContract.BodyTestMatcher(
path: '$.positiveInteger',
type: YamlContract.TestMatcherType.by_regex,
value: '([1-9]\\d*)'),
new YamlContract.BodyTestMatcher(
path: '$.integer',
type: YamlContract.TestMatcherType.by_regex,
value: '-?(\\d+)'),
new YamlContract.BodyTestMatcher(
path: '$.double',
type: YamlContract.TestMatcherType.by_regex,
value: '-?(\\d*\\.\\d+)'),
new YamlContract.BodyTestMatcher(
path: '$.aBoolean',
type: YamlContract.TestMatcherType.by_regex,
value: '(true|false)'),
new YamlContract.BodyTestMatcher(
path: '$.date',
type: YamlContract.TestMatcherType.by_regex,
value: '(\\d\\d\\d\\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])'),
new YamlContract.BodyTestMatcher(
path: '$.dateTime',
type: YamlContract.TestMatcherType.by_regex,
value: '([0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])'),
new YamlContract.BodyTestMatcher(
path: '$.time',
type: YamlContract.TestMatcherType.by_regex,
value: '(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])'),
new YamlContract.BodyTestMatcher(
path: '$.valueWithTypeMatch',
type: YamlContract.TestMatcherType.by_type),
new YamlContract.BodyTestMatcher(
path: '$.valueWithMin',
type: YamlContract.TestMatcherType.by_type,
minOccurrence: 1),
new YamlContract.BodyTestMatcher(
path: '$.valueWithMax',
type: YamlContract.TestMatcherType.by_type,
maxOccurrence: 3),
new YamlContract.BodyTestMatcher(
path: '$.valueWithMinMax',
type: YamlContract.TestMatcherType.by_type,
minOccurrence: 1,
maxOccurrence: 3),
new YamlContract.BodyTestMatcher(
path: '$.valueWithMinEmpty',
type: YamlContract.TestMatcherType.by_type,
minOccurrence: 0),
new YamlContract.BodyTestMatcher(
path: '$.valueWithMaxEmpty',
type: YamlContract.TestMatcherType.by_type,
maxOccurrence: 0),
new YamlContract.BodyTestMatcher(
path: '$.duck',
type: YamlContract.TestMatcherType.by_command,
value: 'assertThatValueIsANumber($it)'),
new YamlContract.BodyTestMatcher(
path: "\$.['key'].['complex.key']",
type: YamlContract.TestMatcherType.by_equality),
new YamlContract.BodyTestMatcher(
path: '$.nullValue',
type: YamlContract.TestMatcherType.by_null),
]
}
}