diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverter.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverter.groovy index ab600fca7a..9f9b759d17 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverter.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverter.groovy @@ -280,7 +280,11 @@ class YamlContractConverter implements ContractConverter> { value = byTimestamp() break case StubMatcherType.by_regex: - value = byRegex(matcher.value) + String regex = matcher.value + if (matcher.predefined) { + regex = predefinedToPattern(matcher.predefined).pattern() + } + value = byRegex(regex) break case StubMatcherType.by_equality: value = byEquality() @@ -320,7 +324,11 @@ class YamlContractConverter implements ContractConverter> { value = byTimestamp() break case TestMatcherType.by_regex: - value = byRegex(testMatcher.value) + String regex = testMatcher.value + if (testMatcher.predefined) { + regex = predefinedToPattern(testMatcher.predefined).pattern() + } + value = byRegex(regex) break case TestMatcherType.by_equality: value = byEquality() diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverterSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverterSpec.groovy index 960c232065..02d67a9506 100644 --- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverterSpec.groovy +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverterSpec.groovy @@ -16,6 +16,8 @@ package org.springframework.cloud.contract.verifier.converter +import spock.lang.Issue + import java.util.regex.Pattern import spock.lang.Shared @@ -54,6 +56,8 @@ class YamlContractConverterSpec extends Specification { File ymlMatchers = new File(ymlMatchersFile.toURI()) URL ymlMultipleFile = YamlContractConverterSpec.getResource("/yml/multiple_contracts.yml") File ymlMultiple = new File(ymlMultipleFile.toURI()) + URL ymlMessagingMatchersFile = YamlContractConverterSpec.getResource("/yml/contract_message_matchers.yml") + File ymlMessagingMatchers = new File(ymlMessagingMatchersFile.toURI()) YamlContractConverter converter = new YamlContractConverter() def "should convert YAML with REST to DSL for [#yamlFile]"() { @@ -228,6 +232,94 @@ class YamlContractConverterSpec extends Specification { contract.response.matchers.jsonPathRegexMatchers[15].value() == new ExecutionProperty('assertThatValueIsANumber($it)') } + @Issue("#604") + def "should convert YAML with Message matchers to DSL"() { + given: + assert converter.isAccepted(ymlMessagingMatchers) + when: + Collection contracts = converter.convertFrom(ymlMessagingMatchers) + then: + contracts.size() == 1 + Contract contract = contracts.first() + RegexPatterns patterns = new RegexPatterns() + contract.input.messageHeaders.entries.find { it.name == "contentType" && + ((Pattern) it.clientValue).pattern == "application/json.*" && it.serverValue == "application/json" } + contract.input.matchers.jsonPathRegexMatchers[0].path() == '$.duck' + contract.input.matchers.jsonPathRegexMatchers[0].matchingType() == MatchingType.REGEX + contract.input.matchers.jsonPathRegexMatchers[0].value() == '[0-9]{3}' + contract.input.matchers.jsonPathRegexMatchers[1].path() == '$.duck' + contract.input.matchers.jsonPathRegexMatchers[1].matchingType() == MatchingType.EQUALITY + contract.input.matchers.jsonPathRegexMatchers[2].path() == '$.alpha' + contract.input.matchers.jsonPathRegexMatchers[2].matchingType() == MatchingType.REGEX + contract.input.matchers.jsonPathRegexMatchers[2].value() == patterns.onlyAlphaUnicode() + contract.input.matchers.jsonPathRegexMatchers[3].path() == '$.alpha' + contract.input.matchers.jsonPathRegexMatchers[3].matchingType() == MatchingType.EQUALITY + contract.input.matchers.jsonPathRegexMatchers[4].path() == '$.number' + contract.input.matchers.jsonPathRegexMatchers[4].matchingType() == MatchingType.REGEX + contract.input.matchers.jsonPathRegexMatchers[4].value() == patterns.number() + contract.input.matchers.jsonPathRegexMatchers[5].path() == '$.aBoolean' + contract.input.matchers.jsonPathRegexMatchers[5].matchingType() == MatchingType.REGEX + contract.input.matchers.jsonPathRegexMatchers[5].value() == patterns.anyBoolean() + contract.input.matchers.jsonPathRegexMatchers[6].path() == '$.date' + contract.input.matchers.jsonPathRegexMatchers[6].matchingType() == MatchingType.DATE + contract.input.matchers.jsonPathRegexMatchers[6].value() == patterns.isoDate() + contract.input.matchers.jsonPathRegexMatchers[7].path() == '$.dateTime' + contract.input.matchers.jsonPathRegexMatchers[7].matchingType() == MatchingType.TIMESTAMP + contract.input.matchers.jsonPathRegexMatchers[7].value() == patterns.isoDateTime() + contract.input.matchers.jsonPathRegexMatchers[8].path() == '$.time' + contract.input.matchers.jsonPathRegexMatchers[8].matchingType() == MatchingType.TIME + contract.input.matchers.jsonPathRegexMatchers[8].value() == patterns.isoTime() + contract.input.matchers.jsonPathRegexMatchers[9].path() == "\$.['key'].['complex.key']" + contract.input.matchers.jsonPathRegexMatchers[9].matchingType() == MatchingType.EQUALITY + and: + contract.outputMessage.matchers.jsonPathRegexMatchers[0].path() == '$.duck' + contract.outputMessage.matchers.jsonPathRegexMatchers[0].matchingType() == MatchingType.REGEX + contract.outputMessage.matchers.jsonPathRegexMatchers[0].value() == '[0-9]{3}' + contract.outputMessage.matchers.jsonPathRegexMatchers[1].path() == '$.duck' + contract.outputMessage.matchers.jsonPathRegexMatchers[1].matchingType() == MatchingType.EQUALITY + contract.outputMessage.matchers.jsonPathRegexMatchers[2].path() == '$.alpha' + contract.outputMessage.matchers.jsonPathRegexMatchers[2].matchingType() == MatchingType.REGEX + contract.outputMessage.matchers.jsonPathRegexMatchers[2].value() == patterns.onlyAlphaUnicode() + contract.outputMessage.matchers.jsonPathRegexMatchers[3].path() == '$.alpha' + contract.outputMessage.matchers.jsonPathRegexMatchers[3].matchingType() == MatchingType.EQUALITY + contract.outputMessage.matchers.jsonPathRegexMatchers[4].path() == '$.number' + contract.outputMessage.matchers.jsonPathRegexMatchers[4].matchingType() == MatchingType.REGEX + contract.outputMessage.matchers.jsonPathRegexMatchers[4].value() == patterns.number() + contract.outputMessage.matchers.jsonPathRegexMatchers[5].path() == '$.aBoolean' + contract.outputMessage.matchers.jsonPathRegexMatchers[5].matchingType() == MatchingType.REGEX + contract.outputMessage.matchers.jsonPathRegexMatchers[5].value() == patterns.anyBoolean() + contract.outputMessage.matchers.jsonPathRegexMatchers[6].path() == '$.date' + contract.outputMessage.matchers.jsonPathRegexMatchers[6].matchingType() == MatchingType.DATE + contract.outputMessage.matchers.jsonPathRegexMatchers[6].value() == patterns.isoDate() + contract.outputMessage.matchers.jsonPathRegexMatchers[7].path() == '$.dateTime' + contract.outputMessage.matchers.jsonPathRegexMatchers[7].matchingType() == MatchingType.TIMESTAMP + contract.outputMessage.matchers.jsonPathRegexMatchers[7].value() == patterns.isoDateTime() + contract.outputMessage.matchers.jsonPathRegexMatchers[8].path() == '$.time' + contract.outputMessage.matchers.jsonPathRegexMatchers[8].matchingType() == MatchingType.TIME + contract.outputMessage.matchers.jsonPathRegexMatchers[8].value() == patterns.isoTime() + contract.outputMessage.matchers.jsonPathRegexMatchers[9].path() == '$.valueWithTypeMatch' + contract.outputMessage.matchers.jsonPathRegexMatchers[9].matchingType() == MatchingType.TYPE + contract.outputMessage.matchers.jsonPathRegexMatchers[10].path() == '$.valueWithMin' + contract.outputMessage.matchers.jsonPathRegexMatchers[10].matchingType() == MatchingType.TYPE + contract.outputMessage.matchers.jsonPathRegexMatchers[10].minTypeOccurrence() == 1 + contract.outputMessage.matchers.jsonPathRegexMatchers[11].path() == '$.valueWithMax' + contract.outputMessage.matchers.jsonPathRegexMatchers[11].matchingType() == MatchingType.TYPE + contract.outputMessage.matchers.jsonPathRegexMatchers[11].maxTypeOccurrence() == 3 + contract.outputMessage.matchers.jsonPathRegexMatchers[12].path() == '$.valueWithMinMax' + contract.outputMessage.matchers.jsonPathRegexMatchers[12].matchingType() == MatchingType.TYPE + contract.outputMessage.matchers.jsonPathRegexMatchers[12].minTypeOccurrence() == 1 + contract.outputMessage.matchers.jsonPathRegexMatchers[12].maxTypeOccurrence() == 3 + contract.outputMessage.matchers.jsonPathRegexMatchers[13].path() == '$.valueWithMinEmpty' + contract.outputMessage.matchers.jsonPathRegexMatchers[13].matchingType() == MatchingType.TYPE + contract.outputMessage.matchers.jsonPathRegexMatchers[13].minTypeOccurrence() == 0 + contract.outputMessage.matchers.jsonPathRegexMatchers[14].path() == '$.valueWithMaxEmpty' + contract.outputMessage.matchers.jsonPathRegexMatchers[14].matchingType() == MatchingType.TYPE + contract.outputMessage.matchers.jsonPathRegexMatchers[14].maxTypeOccurrence() == 0 + contract.outputMessage.matchers.jsonPathRegexMatchers[15].path() == '$.duck' + contract.outputMessage.matchers.jsonPathRegexMatchers[15].matchingType() == MatchingType.COMMAND + contract.outputMessage.matchers.jsonPathRegexMatchers[15].value() == new ExecutionProperty('assertThatValueIsANumber($it)') + } + def "should convert YAML with REST with response from request"() { given: assert converter.isAccepted(ymlBody) diff --git a/spring-cloud-contract-verifier/src/test/resources/yml/contract_message_matchers.yml b/spring-cloud-contract-verifier/src/test/resources/yml/contract_message_matchers.yml new file mode 100644 index 0000000000..07f72ff01f --- /dev/null +++ b/spring-cloud-contract-verifier/src/test/resources/yml/contract_message_matchers.yml @@ -0,0 +1,125 @@ +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' + messageHeaders: + sample: 'header' + contentType: application/json + matchers: + headers: + - key: contentType + regex: "application/json.*" + body: + - path: $.duck + type: by_regex + value: "[0-9]{3}" + - path: $.duck + type: by_equality + - path: $.alpha + type: by_regex + predefined: only_alpha_unicode + - path: $.alpha + type: by_equality + - path: $.number + type: by_regex + predefined: number + - path: $.aBoolean + type: by_regex + predefined: any_boolean + - path: $.date + type: by_date + - path: $.dateTime + type: by_timestamp + - path: $.time + type: by_time + - path: "$.['key'].['complex.key']" + type: by_equality +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' + matchers: + headers: + - key: Content-Type + regex: "application/json.*" + body: + - path: $.duck + type: by_regex + value: "[0-9]{3}" + - path: $.duck + type: by_equality + - path: $.alpha + type: by_regex + predefined: only_alpha_unicode + - path: $.alpha + type: by_equality + - path: $.number + type: by_regex + predefined: number + - path: $.aBoolean + type: by_regex + predefined: any_boolean + - path: $.date + type: by_date + - path: $.dateTime + type: by_timestamp + - path: $.time + type: by_time + - path: $.valueWithTypeMatch + type: by_type + - path: $.valueWithMin + type: by_type + minOccurrence: 1 + - path: $.valueWithMax + type: by_type + maxOccurrence: 3 + - path: $.valueWithMinMax + type: by_type + minOccurrence: 1 + maxOccurrence: 3 + - path: $.valueWithMinEmpty + type: by_type + minOccurrence: 0 + - path: $.valueWithMaxEmpty + type: by_type + maxOccurrence: 0 + - path: $.duck + type: by_command + value: assertThatValueIsANumber($it) + headers: + contentType: application/json \ No newline at end of file