From de372ed2246a4aeef4bbbaa8c8212d1b7f657cdd Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Wed, 6 Nov 2019 12:29:39 +0100 Subject: [PATCH] Fixed the issue that a null value wasn't considered an optional property without this change an optional property was such that had an empty or non empty string. Null wasn't supported with this change we're adding null back fixes gh-1257 --- .../contract/spec/internal/Common.groovy | 4 +- .../spec/internal/ContractSpec.groovy | 24 ++++++++++ .../dsl/wiremock/WireMockGroovyDslSpec.groovy | 48 +++++++++++++++++++ 3 files changed, 74 insertions(+), 2 deletions(-) diff --git a/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/Common.groovy b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/Common.groovy index ff33fcd4d0..b8fca2e9c6 100644 --- a/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/Common.groovy +++ b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/Common.groovy @@ -233,7 +233,7 @@ class Common { } void assertThatSidesMatch(OptionalProperty stubSide, Object testSide) { - assert testSide ==~ Pattern.compile(stubSide.optionalPattern()) + assert testSide == null || testSide ==~ Pattern.compile(stubSide.optionalPattern()) } void assertThatSidesMatch(Pattern pattern, String value) { @@ -245,7 +245,7 @@ class Common { } void assertThatSidesMatch(MatchingStrategy firstSide, MatchingStrategy secondSide) { - if (firstSide.type == MatchingStrategy.Type.ABSENT && secondSide != MatchingStrategy.Type.ABSENT) { + if (firstSide.type == MatchingStrategy.Type.ABSENT && secondSide.type != MatchingStrategy.Type.ABSENT) { throwAbsentError() } } diff --git a/spring-cloud-contract-spec/src/test/groovy/org/springframework/cloud/contract/spec/internal/ContractSpec.groovy b/spring-cloud-contract-spec/src/test/groovy/org/springframework/cloud/contract/spec/internal/ContractSpec.groovy index 4c36c70029..de2506e850 100644 --- a/spring-cloud-contract-spec/src/test/groovy/org/springframework/cloud/contract/spec/internal/ContractSpec.groovy +++ b/spring-cloud-contract-spec/src/test/groovy/org/springframework/cloud/contract/spec/internal/ContractSpec.groovy @@ -398,4 +398,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 aaa19436ad..b73707243c 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 @@ -2713,6 +2713,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()) } @@ -2732,6 +2773,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"))