diff --git a/specs/spring-cloud-contract-spec-java/src/main/java/org/springframework/cloud/contract/spec/internal/Common.java b/specs/spring-cloud-contract-spec-java/src/main/java/org/springframework/cloud/contract/spec/internal/Common.java index b0e22c6be7..51115a4796 100644 --- a/specs/spring-cloud-contract-spec-java/src/main/java/org/springframework/cloud/contract/spec/internal/Common.java +++ b/specs/spring-cloud-contract-spec-java/src/main/java/org/springframework/cloud/contract/spec/internal/Common.java @@ -275,6 +275,9 @@ public class Common { public void assertThatSidesMatch(Object firstSide, Object secondSide) { if (firstSide instanceof OptionalProperty) { + if (secondSide == null) { + return; + } assertThat( secondSide.toString() .matches(((OptionalProperty) firstSide).optionalPattern()), diff --git a/specs/spring-cloud-contract-spec/src/test/groovy/org/springframework/cloud/contract/spec/internal/ContractSpec.groovy b/specs/spring-cloud-contract-spec/src/test/groovy/org/springframework/cloud/contract/spec/internal/ContractSpec.groovy index ee75ef458f..b54cc0a04e 100644 --- a/specs/spring-cloud-contract-spec/src/test/groovy/org/springframework/cloud/contract/spec/internal/ContractSpec.groovy +++ b/specs/spring-cloud-contract-spec/src/test/groovy/org/springframework/cloud/contract/spec/internal/ContractSpec.groovy @@ -421,4 +421,28 @@ then: assertThat(contract.request.bodyMatchers.hasMatchers()).isTrue() assertThat(contract.response.bodyMatchers.hasMatchers()).isTrue() } + + def 'should work with optional and null value of a field'() { + given: + def contract = Contract.make { + description("Creating user") + name("Create user") + request { + method 'POST' + url '/api/user' + body( + address: $(consumer(optional(regex(alphaNumeric()))), producer(null)), + name: $(consumer(optional(regex(alphaNumeric()))), producer('')) + ) + headers { + contentType(applicationJson()) + } + } + response { + status 201 + } + } + expect: + contract != null + } } diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockGroovyDslSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockGroovyDslSpec.groovy index 3a3ad28411..4db244d4f8 100755 --- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockGroovyDslSpec.groovy +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockGroovyDslSpec.groovy @@ -2862,6 +2862,47 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie } + @Issue("#1257") + def "should work with null request element on the client side and optional stub entry"() { + given: + Contract contractDsl = Contract.make { + description("Creating user") + name("Create user") + request { + method 'POST' + url '/api/user' + body( + address: $(consumer(optional(regex(alphaNumeric()))), producer(null)), + name: $(consumer(optional(regex(alphaNumeric()))), producer('')) + ) + headers { + contentType(applicationJson()) + } + } + response { + status 201 + } + } + and: + String wireMockStub = new WireMockStubStrategy("Test", + new ContractMetadata(null, false, 0, null, contractDsl), contractDsl) + .toWireMockClientStub() + + and: + int port = SocketUtils.findAvailableTcpPort() + WireMockServer server = new WireMockServer(config().port(port)) + server.start() + and: + stubMappingIsValidWireMockStub(wireMockStub) + server.addStubMapping(WireMockStubMapping.buildFrom(wireMockStub)) + when: + ResponseEntity entity = callWithOptionalAndEmpty(port) + then: + entity.statusCodeValue == 201 + cleanup: + server?.shutdown() + } + WireMockConfiguration config() { return new WireMockConfiguration().extensions(responseTemplateTransformer()) } @@ -2882,6 +2923,13 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie .body("{\"foo\":\"bar\",\"baz\":5}"), String.class) } + ResponseEntity callWithOptionalAndEmpty(int port) { + return new TestRestTemplate().exchange( + RequestEntity.post(URI.create("http://localhost:" + port + "/api/user")) + .header("Content-Type", "application/json") + .body("{\"foo\":null,\"name\":\"\"}"), String.class) + } + ResponseEntity callBytes(int port, File request) { return new TestRestTemplate().exchange( RequestEntity.put(URI.create("http://localhost:" + port + "/1"))