spring-cloud-contract
spring-cloud-contract copied to clipboard
OptionalProperty produces wrong output with ClientDslProperty & ServerDslProperty
Bug description
optional
around ClientDslProperty
/ServerDslProperty
properties such as anyOf
, anyNonBlankString
, anyEmail
, etc. produce wrong output, like OPTIONAL>>ClientDslProperty{\nclientValue=^FOO$|^BAR$, \n\tserverValue=FOO}<<
Version affected: at least 3.1.3
Sample
Sample repository: https://github.com/artemy/spring-cloud-contract-demo
Example contract
package contracts
import org.springframework.cloud.contract.spec.ContractDsl.Companion.contract
contract {
request {
method = GET
url = url("/api")
headers {
contentType = APPLICATION_JSON
accept = APPLICATION_JSON
}
body = body(
"foo" to value(consumer(optional(anyOf("FOO", "BAR"))), producer("FOO")),
"bar" to value(consumer(optional(anyNonBlankString)), producer("FOO")),
"baz" to value(consumer(optional(nonBlank)), producer("BAZ"))
)
}
response {
status = OK
headers {
contentType = APPLICATION_JSON
}
body = body(
"foo" to value(consumer("FOO"), producer(optional(anyOf("FOO", "BAR")))),
"bar" to value(consumer("FOO"), producer(optional(anyNonBlankString))),
"baz" to value(consumer("BAZ"), producer(optional(nonBlank)))
)
}
}
Expected result:
generated test
package com.github.artemy.springcloudcontractdemo;
import com.github.artemy.springcloudcontractdemo.ContractVerifierBaseIT;
import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.JsonPath;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import io.restassured.module.mockmvc.specification.MockMvcRequestSpecification;
import io.restassured.response.ResponseOptions;
import static org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat;
import static org.springframework.cloud.contract.verifier.util.ContractVerifierUtil.*;
import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson;
import static io.restassured.module.mockmvc.RestAssuredMockMvc.*;
@SuppressWarnings("rawtypes")
public class ContractVerifierTest extends ContractVerifierBaseIT {
@Test
public void validate_example_contract_kotlin() throws Exception {
// given:
MockMvcRequestSpecification request = given()
.header("Content-Type", "application/json")
.header("Accept", "application/json")
.body("{\"foo\":\"FOO\",\"bar\":\"FOO\",\"baz\":\"BAZ\"}");
// when:
ResponseOptions response = given().spec(request)
.get("/api");
// then:
assertThat(response.statusCode()).isEqualTo(200);
assertThat(response.header("Content-Type")).matches("application/json.*");
// and:
DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
assertThatJson(parsedJson).field("['foo']").matches("(^FOO$|^BAR$)?");
assertThatJson(parsedJson).field("['bar']").matches("(^\\s*\\S[\\S\\s]*)?");
assertThatJson(parsedJson).field("['baz']").matches("(^\\s*\\S[\\S\\s]*)?");
}
}
generated stub
{
"id" : "4079dae6-0ce0-4f85-b9bf-35662d19315e",
"request" : {
"url" : "/api",
"method" : "GET",
"headers" : {
"Content-Type" : {
"matches" : "application/json.*"
},
"Accept" : {
"matches" : "application/json.*"
}
},
"bodyPatterns" : [ {
"matchesJsonPath" : "$[?(@.['foo'] =~ /(^FOO$|^BAR$)?/)]"
}, {
"matchesJsonPath" : "$[?(@.['bar'] =~ /(^\\s*\\S[\\S\\s]*)?/)]"
}, {
"matchesJsonPath" : "$[?(@.['baz'] =~ /(^\\s*\\S[\\S\\s]*)?/)]"
} ]
},
"response" : {
"status" : 200,
"body" : "{\"foo\":\"FOO\",\"bar\":\"FOO\",\"baz\":\"BAZ\"}",
"headers" : {
"Content-Type" : "application/json"
},
"transformers" : [ "response-template", "spring-cloud-contract" ]
},
"uuid" : "4079dae6-0ce0-4f85-b9bf-35662d19315e"
}
Actual result:
generated test
package com.github.artemy.springcloudcontractdemo;
import com.github.artemy.springcloudcontractdemo.ContractVerifierBaseIT;
import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.JsonPath;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import io.restassured.module.mockmvc.specification.MockMvcRequestSpecification;
import io.restassured.response.ResponseOptions;
import static org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat;
import static org.springframework.cloud.contract.verifier.util.ContractVerifierUtil.*;
import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson;
import static io.restassured.module.mockmvc.RestAssuredMockMvc.*;
@SuppressWarnings("rawtypes")
public class ContractVerifierTest extends ContractVerifierBaseIT {
@Test
public void validate_example_contract_kotlin() throws Exception {
// given:
MockMvcRequestSpecification request = given()
.header("Content-Type", "application/json")
.header("Accept", "application/json")
.body("{\"foo\":\"FOO\",\"bar\":\"FOO\",\"baz\":\"BAZ\"}");
// when:
ResponseOptions response = given().spec(request)
.get("/api");
// then:
assertThat(response.statusCode()).isEqualTo(200);
assertThat(response.header("Content-Type")).matches("application/json.*");
// and:
DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
assertThatJson(parsedJson).field("['foo']").isEqualTo("OPTIONAL>>ServerDslProperty{\nclientValue=FOO, \n\tserverValue=^FOO$|^BAR$}<<");
assertThatJson(parsedJson).field("['bar']").isEqualTo("OPTIONAL>>ServerDslProperty{\nclientValue=ESFMJYTTPZOBTXNKOBPA, \n\tserverValue=^\\s*\\S[\\S\\s]*}<<");
assertThatJson(parsedJson).field("['baz']").matches("(^\\s*\\S[\\S\\s]*)?");
}
}
generated stub
{
"id" : "52a4282f-1ec2-46cc-bb8e-665a3b672496",
"request" : {
"url" : "/api",
"method" : "GET",
"headers" : {
"Content-Type" : {
"matches" : "application/json.*"
},
"Accept" : {
"matches" : "application/json.*"
}
},
"bodyPatterns" : [ {
"matchesJsonPath" : "$[?(@.['foo'] == 'OPTIONAL>>ClientDslProperty{\nclientValue=^FOO$|^BAR$, \n\tserverValue=FOO}<<')]"
}, {
"matchesJsonPath" : "$[?(@.['bar'] == 'OPTIONAL>>ClientDslProperty{\nclientValue=^\\s*\\S[\\S\\s]*, \n\tserverValue=NGLOGRZLBZZKSDKSVDRL}<<')]"
}, {
"matchesJsonPath" : "$[?(@.['baz'] =~ /(^\\s*\\S[\\S\\s]*)?/)]"
} ]
},
"response" : {
"status" : 200,
"body" : "{\"foo\":\"FOO\",\"bar\":\"FOO\",\"baz\":\"BAZ\"}",
"headers" : {
"Content-Type" : "application/json"
},
"transformers" : [ "response-template", "spring-cloud-contract" ]
},
"uuid" : "52a4282f-1ec2-46cc-bb8e-665a3b672496"
}