diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JUnitMessagingMethodBodyBuilder.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JUnitMessagingMethodBodyBuilder.groovy index ded05264a7..ddd48ff25f 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JUnitMessagingMethodBodyBuilder.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JUnitMessagingMethodBodyBuilder.groovy @@ -24,7 +24,9 @@ import org.springframework.cloud.contract.spec.internal.ExecutionProperty import org.springframework.cloud.contract.spec.internal.Header import org.springframework.cloud.contract.spec.internal.Input import org.springframework.cloud.contract.spec.internal.NamedProperty +import org.springframework.cloud.contract.spec.internal.NotToEscapePattern import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties +import org.springframework.cloud.contract.verifier.util.MapConverter import java.util.regex.Pattern @@ -92,6 +94,12 @@ class JUnitMessagingMethodBodyBuilder extends MessagingMethodBodyBuilder { blockBuilder.addLine("assertThat(response.getHeader(\"$property\").toString()).${createHeaderComparison(value)}") } + @Override + protected void processHeaderElement(BlockBuilder blockBuilder, String property, Number value) { + blockBuilder.addLine("assertThat(response.getHeader(\"$property\")).isNotNull();") + blockBuilder.addLine("assertThat(response.getHeader(\"$property\")).isEqualTo(${value});") + } + @Override protected void processHeaderElement(BlockBuilder blockBuilder, String property, Pattern pattern) { blockBuilder.addLine("assertThat(response.getHeader(\"$property\")).isNotNull();") @@ -114,7 +122,9 @@ class JUnitMessagingMethodBodyBuilder extends MessagingMethodBodyBuilder { bb.addLine("""ContractVerifierMessage response = contractVerifierMessaging.receive("${outputMessage.sentTo.serverValue}");""") bb.addLine("""assertThat(response).isNotNull();""") outputMessage.headers?.executeForEachHeader { Header header -> - processHeaderElement(bb, header.name, header.serverValue) + processHeaderElement(bb, header.name, header.serverValue instanceof NotToEscapePattern ? + header.serverValue : + MapConverter.getTestSideValues(header.serverValue)) } } diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JaxRsClientJUnitMethodBodyBuilder.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JaxRsClientJUnitMethodBodyBuilder.groovy index c79c698ff7..10148e0d52 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JaxRsClientJUnitMethodBodyBuilder.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JaxRsClientJUnitMethodBodyBuilder.groovy @@ -21,6 +21,7 @@ import groovy.transform.TypeChecked import org.springframework.cloud.contract.spec.Contract import org.springframework.cloud.contract.spec.internal.DslProperty import org.springframework.cloud.contract.spec.internal.Header +import org.springframework.cloud.contract.spec.internal.NotToEscapePattern import org.springframework.cloud.contract.spec.internal.QueryParameter import org.springframework.cloud.contract.spec.internal.ExecutionProperty import org.springframework.cloud.contract.spec.internal.QueryParameters @@ -136,7 +137,9 @@ class JaxRsClientJUnitMethodBodyBuilder extends JUnitMethodBodyBuilder { @Override protected void validateResponseHeadersBlock(BlockBuilder bb) { response.headers?.executeForEachHeader { Header header -> - processHeaderElement(bb, header.name, header.serverValue) + processHeaderElement(bb, header.name, header.serverValue instanceof NotToEscapePattern ? + header.serverValue : + MapConverter.getTestSideValues(header.serverValue)) } } @@ -149,11 +152,26 @@ class JaxRsClientJUnitMethodBodyBuilder extends JUnitMethodBodyBuilder { return 'responseAsString' } + @Override + protected void processHeaderElement(BlockBuilder blockBuilder, String property, Object value) { + if (value instanceof NotToEscapePattern) { + blockBuilder.addLine("assertThat(response.getHeaderString(\"$property\")).${createHeaderComparison(((NotToEscapePattern) value).serverValue)}") + } else { + // fallback + processHeaderElement(blockBuilder, property, value.toString()) + } + } + @Override protected void processHeaderElement(BlockBuilder blockBuilder, String property, String value) { blockBuilder.addLine("assertThat(response.getHeaderString(\"$property\")).${createHeaderComparison(value)}") } + @Override + protected void processHeaderElement(BlockBuilder blockBuilder, String property, Number value) { + blockBuilder.addLine("assertThat(response.getHeaderString(\"$property\")).isEqualTo(${value});") + } + @Override protected void processHeaderElement(BlockBuilder blockBuilder, String property, Pattern pattern) { blockBuilder.addLine("assertThat(response.getHeaderString(\"$property\")).${createHeaderComparison(pattern)}") diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JaxRsClientSpockMethodRequestProcessingBodyBuilder.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JaxRsClientSpockMethodRequestProcessingBodyBuilder.groovy index d1fd85da40..a18fcbe7dd 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JaxRsClientSpockMethodRequestProcessingBodyBuilder.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JaxRsClientSpockMethodRequestProcessingBodyBuilder.groovy @@ -21,6 +21,7 @@ import groovy.transform.TypeChecked import org.springframework.cloud.contract.spec.Contract import org.springframework.cloud.contract.spec.internal.DslProperty import org.springframework.cloud.contract.spec.internal.Header +import org.springframework.cloud.contract.spec.internal.NotToEscapePattern import org.springframework.cloud.contract.spec.internal.QueryParameter import org.springframework.cloud.contract.spec.internal.QueryParameters import org.springframework.cloud.contract.spec.internal.ExecutionProperty @@ -136,7 +137,9 @@ class JaxRsClientSpockMethodRequestProcessingBodyBuilder extends SpockMethodRequ @Override protected void validateResponseHeadersBlock(BlockBuilder bb) { response.headers?.executeForEachHeader { Header header -> - processHeaderElement(bb, header.name, header.serverValue) + processHeaderElement(bb, header.name, header.serverValue instanceof NotToEscapePattern ? + header.serverValue : + MapConverter.getTestSideValues(header.serverValue)) } } @@ -145,6 +148,16 @@ class JaxRsClientSpockMethodRequestProcessingBodyBuilder extends SpockMethodRequ return 'responseAsString' } + @Override + protected void processHeaderElement(BlockBuilder blockBuilder, String property, Object value) { + if (value instanceof NotToEscapePattern) { + blockBuilder.addLine("response.getHeaderString('$property') ${convertHeaderComparison(((NotToEscapePattern) value).serverValue)}") + } else { + // fallback + processHeaderElement(blockBuilder, property, value.toString()) + } + } + @Override protected void processHeaderElement(BlockBuilder blockBuilder, String property, ExecutionProperty exec) { blockBuilder.addLine("${exec.insertValue("response.getHeaderString(\'$property\')")}") @@ -155,6 +168,11 @@ class JaxRsClientSpockMethodRequestProcessingBodyBuilder extends SpockMethodRequ blockBuilder.addLine("response.getHeaderString('$property') ${convertHeaderComparison(value)}") } + @Override + protected void processHeaderElement(BlockBuilder blockBuilder, String property, Number value) { + blockBuilder.addLine("response.getHeaderString('$property') == ${value}") + } + @Override protected void processHeaderElement(BlockBuilder blockBuilder, String property, Pattern value) { blockBuilder.addLine("response.getHeaderString('$property') ${convertHeaderComparison(value)}") diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilder.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilder.groovy index 7a8ad2eaa7..9d31fcff7c 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilder.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilder.groovy @@ -165,6 +165,11 @@ abstract class MethodBodyBuilder { */ protected abstract void processHeaderElement(BlockBuilder blockBuilder, String property, GString value) + /** + * Appends to the {@link BlockBuilder} the assertion for the given header path + */ + protected abstract void processHeaderElement(BlockBuilder blockBuilder, String property, Number value) + /** * Appends to the {@link BlockBuilder} the code to retrieve a value for a property * from the list with the given index diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcSpockMethodRequestProcessingBodyBuilder.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcSpockMethodRequestProcessingBodyBuilder.groovy index bbca85cfeb..1a35744196 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcSpockMethodRequestProcessingBodyBuilder.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcSpockMethodRequestProcessingBodyBuilder.groovy @@ -23,6 +23,7 @@ import org.springframework.cloud.contract.spec.internal.ExecutionProperty import org.springframework.cloud.contract.spec.internal.Header import org.springframework.cloud.contract.spec.internal.NotToEscapePattern import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties +import org.springframework.cloud.contract.verifier.util.MapConverter import java.util.regex.Pattern /** @@ -46,7 +47,9 @@ class MockMvcSpockMethodRequestProcessingBodyBuilder extends SpockMethodRequestP @Override protected void validateResponseHeadersBlock(BlockBuilder bb) { response.headers?.executeForEachHeader { Header header -> - processHeaderElement(bb, header.name, header.serverValue) + processHeaderElement(bb, header.name, header.serverValue instanceof NotToEscapePattern ? + header.serverValue : + MapConverter.getTestSideValues(header.serverValue)) } } @@ -59,10 +62,18 @@ class MockMvcSpockMethodRequestProcessingBodyBuilder extends SpockMethodRequestP protected void processHeaderElement(BlockBuilder blockBuilder, String property, Object value) { if (value instanceof NotToEscapePattern) { blockBuilder.addLine("response.header('$property') " + - "${patternComparison(value.serverValue.pattern().replace("\\", "\\\\"))}") + "${patternComparison(((NotToEscapePattern) value).serverValue.pattern().replace("\\", "\\\\"))}") + } else { + // fallback + processHeaderElement(blockBuilder, property, value.toString()) } } + @Override + protected void processHeaderElement(BlockBuilder blockBuilder, String property, Number number) { + blockBuilder.addLine("response.header('$property') == ${number}") + } + @Override protected void processHeaderElement(BlockBuilder blockBuilder, String property, ExecutionProperty exec) { blockBuilder.addLine("${exec.insertValue("response.header(\'$property\')")}") diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/RestAssuredJUnitMethodBodyBuilder.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/RestAssuredJUnitMethodBodyBuilder.groovy index b679c69b58..c37fe36681 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/RestAssuredJUnitMethodBodyBuilder.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/RestAssuredJUnitMethodBodyBuilder.groovy @@ -23,9 +23,9 @@ import org.springframework.cloud.contract.spec.internal.ExecutionProperty import org.springframework.cloud.contract.spec.internal.Header import org.springframework.cloud.contract.spec.internal.NotToEscapePattern import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties +import org.springframework.cloud.contract.verifier.util.MapConverter import java.util.regex.Pattern - /** * A {@link JUnitMethodBodyBuilder} implementation that uses Rest Assured. * @@ -48,8 +48,10 @@ class RestAssuredJUnitMethodBodyBuilder extends JUnitMethodBodyBuilder { @Override protected void validateResponseHeadersBlock(BlockBuilder bb) { - response.headers?.executeForEachHeader { Header header ->\ - processHeaderElement(bb, header.name, header.serverValue) + response.headers?.executeForEachHeader { Header header -> + processHeaderElement(bb, header.name, header.serverValue instanceof NotToEscapePattern ? + header.serverValue : + MapConverter.getTestSideValues(header.serverValue)) } } @@ -68,6 +70,9 @@ class RestAssuredJUnitMethodBodyBuilder extends JUnitMethodBodyBuilder { if (value instanceof NotToEscapePattern) { blockBuilder.addLine("assertThat(response.header(\"$property\"))." + "${createMatchesMethod((value as NotToEscapePattern).serverValue.pattern().replace("\\", "\\\\"))};") + } else { + // fallback + processHeaderElement(blockBuilder, property, value.toString()) } } @@ -76,6 +81,11 @@ class RestAssuredJUnitMethodBodyBuilder extends JUnitMethodBodyBuilder { blockBuilder.addLine("assertThat(response.header(\"$property\")).${createHeaderComparison(value)}") } + @Override + protected void processHeaderElement(BlockBuilder blockBuilder, String property, Number value) { + blockBuilder.addLine("assertThat(response.header(\"$property\")).isEqualTo(${value});") + } + @Override protected void processHeaderElement(BlockBuilder blockBuilder, String property, Pattern pattern) { blockBuilder.addLine("assertThat(response.header(\"$property\")).${createHeaderComparison(pattern)}") diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/SpockMessagingMethodBodyBuilder.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/SpockMessagingMethodBodyBuilder.groovy index 6782af0463..2cf0fc3732 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/SpockMessagingMethodBodyBuilder.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/SpockMessagingMethodBodyBuilder.groovy @@ -24,7 +24,9 @@ import org.springframework.cloud.contract.spec.internal.ExecutionProperty import org.springframework.cloud.contract.spec.internal.Header import org.springframework.cloud.contract.spec.internal.Input import org.springframework.cloud.contract.spec.internal.NamedProperty +import org.springframework.cloud.contract.spec.internal.NotToEscapePattern import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties +import org.springframework.cloud.contract.verifier.util.MapConverter import java.util.regex.Pattern /** @@ -81,6 +83,11 @@ class SpockMessagingMethodBodyBuilder extends MessagingMethodBodyBuilder { blockBuilder.addLine("response.getHeader('$property')?.toString() ${convertHeaderComparison(value)}") } + @Override + protected void processHeaderElement(BlockBuilder blockBuilder, String property, Number value) { + blockBuilder.addLine("response.getHeader('$property') == ${value}") + } + @Override protected void processHeaderElement(BlockBuilder blockBuilder, String property, Pattern value) { blockBuilder.addLine("response.getHeader('$property')?.toString() ${convertHeaderComparison(value)}") @@ -99,7 +106,9 @@ class SpockMessagingMethodBodyBuilder extends MessagingMethodBodyBuilder { @Override protected void validateResponseHeadersBlock(BlockBuilder bb) { outputMessage.headers?.executeForEachHeader { Header header -> - processHeaderElement(bb, header.name, header.serverValue) + processHeaderElement(bb, header.name, header.serverValue instanceof NotToEscapePattern ? + header.serverValue : + MapConverter.getTestSideValues(header.serverValue)) } } diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilderSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilderSpec.groovy index a27f3ddff9..5a9cedf26c 100644 --- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilderSpec.groovy +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilderSpec.groovy @@ -240,4 +240,45 @@ DocumentContext parsedJson = JsonPath.parse(json); "JaxRsClientJUnitMethodBodyBuilder" | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) } } + @Issue('#318') + def "should assert the response headers properly [#methodBuilderName]"() { + given: + def contractDsl = org.springframework.cloud.contract.spec.Contract.make { + request { + method 'POST' + urlPath '/documents/app_statement_v1' + headers { + contentType(applicationPdf()) + } + body([ + PESEL: "77100604360", + CLIENT_NAME: "STANISLAW STASZIC", + STATEMENT_NUMBER: "00200001/C4/2017/1" + ]) + } + response { + status 200 + headers { + contentType(applicationPdf()) + header('Content-Length': 4) + } + + } + } + MethodBodyBuilder builder = methodBuilder(contractDsl) + BlockBuilder blockBuilder = new BlockBuilder(" ") + when: + builder.appendTo(blockBuilder) + def test = blockBuilder.toString() + then: + SyntaxChecker.tryToCompile(methodBuilderName, test) + asserter(test) + where: + methodBuilderName | methodBuilder | asserter + "MockMvcSpockMethodBuilder" | { Contract dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl, properties) } | { String testBody -> testBody.contains("response.header('Content-Length') == 4") && testBody.contains("response.header('Content-Type') ==~ java.util.regex.Pattern.compile('application/pdf.*')") } + "MockMvcJUnitMethodBuilder" | { Contract dsl -> new MockMvcJUnitMethodBodyBuilder(dsl, properties) } | { String testBody -> testBody.contains('assertThat(response.header("Content-Length")).isEqualTo(4);') && testBody.contains('assertThat(response.header("Content-Type")).matches("application/pdf.*");') } + "JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { Contract dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl, properties) } | { String testBody -> testBody.contains("response.getHeaderString('Content-Length') == 4") && testBody.contains(" response.getHeaderString('Content-Type') ==~ java.util.regex.Pattern.compile('application/pdf.*')") } + "JaxRsClientJUnitMethodBodyBuilder" | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) } | { String testBody -> testBody.contains('assertThat(response.getHeaderString("Content-Length")).isEqualTo(4);') && testBody.contains('assertThat(response.getHeaderString("Content-Type")).matches("application/pdf.*");') } + } + }