Merge branch '1.2.x'

This commit is contained in:
Marcin Grzejszczak
2018-04-11 12:48:22 +02:00
39 changed files with 1041 additions and 10 deletions

View File

@@ -257,6 +257,22 @@ include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=request,i
include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=headers,indent=0]
----
`request` may contain additional **request cookies**, as shown in the following example:
.Groovy DSL
[source,groovy,indent=0]
----
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/ContractHttpDocsSpec.groovy[tags=cookies,indent=0]
----
.YAML
[source,yml,indent=0]
----
include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=request,indent=0]
...
include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=cookies,indent=0]
----
`request` may contain a **request body**:
.Groovy DSL
@@ -357,7 +373,7 @@ include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=response,
include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=response_obligatory,indent=0]
----
Besides status, the response may contain **headers** and a **body**, both of which are
Besides status, the response may contain **headers**, **cookies** and a **body**, both of which are
specified the same way as in the request (see the previous paragraph).
TIP: Via the Groovy DSL you can reference the `org.springframework.cloud.contract.spec.internal.HttpStatus`

View File

@@ -86,6 +86,17 @@ public class LoanApplicationService {
return response.getBody().getCount();
}
public String getCookies() {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("Cookie", "name=foo");
httpHeaders.add("Cookie", "name2=bar");
ResponseEntity<String> response =
restTemplate.exchange("http://localhost:" + port + "/frauds/name", HttpMethod.GET,
new HttpEntity<>(httpHeaders),
String.class);
return response.getBody();
}
public void setPort(int port) {
this.port = port;
}

View File

@@ -73,4 +73,12 @@ public class LoanApplicationServiceTests {
assertThat(count).isEqualTo(100);
}
@Test
public void shouldSuccessfullyGetCookies() {
// when:
String cookies = service.getCookies();
// then:
assertThat(cookies).isEqualTo("foo bar");
}
}

View File

@@ -1,5 +1,7 @@
package com.example.fraud;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@@ -24,6 +26,12 @@ class FraudNameController {
}
return new NameResponse("Don't worry " + request.getName() + " you're not a fraud");
}
@GetMapping(value = "/frauds/name")
public String checkByName(@CookieValue("name") String value,
@CookieValue("name2") String value2) {
return value + " " + value2;
}
}
interface FraudVerifier {

View File

@@ -0,0 +1,16 @@
package contracts.fraudname
org.springframework.cloud.contract.spec.Contract.make {
request {
method GET()
url '/frauds/name'
cookies {
cookie("name", "foo")
cookie(name2: "bar")
}
}
response {
status 200
body("foo bar")
}
}

View File

@@ -64,6 +64,12 @@ interface ContractTemplate {
*/
String header(String key, int index)
/**
* Retruns the tempalte for retrieving the first value of a cookie with certain key
* @param key
*/
String cookie(String key)
/**
* Request body text (avoid for non-text bodies) e.g. {{{ request.body }}} . The body will not be escaped
* so you won't be able to directly embed it in a JSON for example.

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.spec.internal
import groovy.transform.CompileStatic
import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString
/**
* Represents a http cookie
*
* @author Alex Xandra Albert Sim
* @since 1.2.5
*/
@EqualsAndHashCode(includeFields = true, callSuper = true)
@ToString(includePackage = false, includeFields = true, ignoreNulls = true, includeNames = true, includeSuper = true)
@CompileStatic
class Cookie extends DslProperty {
String key
Cookie(String key, DslProperty dslProperty) {
super(dslProperty.clientValue, dslProperty.serverValue)
this.key = key
}
Cookie(String key, MatchingStrategy value) {
super(value)
this.key = key
}
Cookie(String key, Object value) {
super(value)
this.key = key
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.spec.internal
import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString
import groovy.transform.TypeChecked
/**
* Represents a set of http cookies
*
* @author Alex Xandra Albert Sim
* @since 1.2.5
*/
@EqualsAndHashCode(includeFields = true)
@ToString(includePackage = false, includeFields = true, ignoreNulls = true, includeNames = true)
@TypeChecked
class Cookies {
Set<Cookie> entries = []
void cookie(Map<String, Object> singleCookie) {
Map.Entry<String, Object> first = singleCookie.entrySet().first()
entries << new Cookie(first?.key, first?.value)
}
void cookie(String cookieKey, Object cookieValue) {
entries << new Cookie(cookieKey, cookieValue)
}
void executeForEachCookie(Closure closure) {
entries?.each {
cookie -> closure(cookie)
}
}
DslProperty matching(String value) {
return new DslProperty(value)
}
boolean equals(o) {
if (this.is(o)) return true
if (getClass() != o.class) return false
Cookies cookies = (Cookies) o
if (cookies != cookies.entries) return false
return true
}
int hashCode() {
return entries.hashCode()
}
/**
* Converts the headers into their stub side representations and returns as
* a map of String key => Object value.
*/
Map<String , Object> asStubSideMap() {
def acc = [:].withDefault { [] as Collection<Object> }
return entries.inject(acc as Map<String, Object>) { Map<String, Object> map, Cookie cookie ->
map[cookie.key] = cookie.clientValue
return map
} as Map<String , Object>
}
/**
* Converts the headers into their stub side representations and returns as
* a map of String key => Object value.
*/
Map<String , Object> asTestSideMap() {
def acc = [:].withDefault { [] as Collection<Object> }
return entries.inject(acc as Map<String, Object>) { Map<String, Object> map, Cookie cookie ->
map[cookie.key] = cookie.serverValue
return map
} as Map<String , Object>
}
}

View File

@@ -77,6 +77,14 @@ class FromRequest {
return new DslProperty(template.header(key, index))
}
/**
* Retruns the tempalte for retrieving the first value of a cookie with certain key
* @param key
*/
DslProperty cookie(String key) {
return new DslProperty(template.cookie(key))
}
/**
* Request body text (avoid for non-text bodies)
*/

View File

@@ -58,6 +58,11 @@ class HandlebarsContractTemplate implements ContractTemplate {
return wrapped("request.headers.${key}.[${index}]")
}
@Override
String cookie(String key) {
return wrapped("request.cookies.${key}")
}
@Override
String body() {
return wrapped("request.body")

View File

@@ -43,6 +43,7 @@ class OutputMessage extends Common {
DslProperty<String> sentTo
Headers headers
Cookies cookies
DslProperty body
ExecutionProperty assertThat
ResponseBodyMatchers bodyMatchers
@@ -52,6 +53,7 @@ class OutputMessage extends Common {
OutputMessage(OutputMessage outputMessage) {
this.sentTo = outputMessage.sentTo
this.headers = outputMessage.headers
this.cookies = outputMessage.cookies
this.body = outputMessage.body
}
@@ -77,6 +79,12 @@ class OutputMessage extends Common {
closure()
}
void cookies(@DelegatesTo(Cookies) Closure closure) {
this.cookies = new Cookies()
closure.delegate = cookies
closure()
}
void assertThat(String assertThat) {
this.assertThat = new ExecutionProperty(assertThat)
}

View File

@@ -47,6 +47,7 @@ class Request extends Common {
Url url
UrlPath urlPath
Headers headers
Cookies cookies
Body body
Multipart multipart
BodyMatchers bodyMatchers
@@ -59,6 +60,7 @@ class Request extends Common {
this.url = request.url
this.urlPath = request.urlPath
this.headers = request.headers
this.cookies = request.cookies
this.body = request.body
this.multipart = request.multipart
}
@@ -121,6 +123,12 @@ class Request extends Common {
closure()
}
void cookies(@DelegatesTo(RequestCookies) Closure closure) {
this.cookies = new RequestCookies()
closure.delegate = cookies
closure()
}
void body(Map<String, Object> body) {
this.body = new Body(convertObjectsToDslProperties(body))
}
@@ -272,6 +280,18 @@ class Request extends Common {
}
}
@CompileStatic
@EqualsAndHashCode(includeFields = true)
@ToString(includePackage = false)
private class RequestCookies extends Cookies {
@Override
DslProperty matching(String value) {
return $(c(regex("${RegexpUtils.escapeSpecialRegexWithSingleEscape(value)}.*")),
p(value))
}
}
@CompileStatic
@EqualsAndHashCode(includeFields = true)
@ToString(includePackage = false)

View File

@@ -46,6 +46,7 @@ class Response extends Common {
DslProperty status
DslProperty delay
Headers headers
Cookies cookies
Body body
boolean async
ResponseBodyMatchers bodyMatchers
@@ -56,6 +57,7 @@ class Response extends Common {
Response(Response response) {
this.status = response.status
this.headers = response.headers
this.cookies = response.cookies
this.body = response.body
}
@@ -73,6 +75,12 @@ class Response extends Common {
closure()
}
void cookies(@DelegatesTo(ResponseCookies) Closure closure) {
this.cookies = new ResponseCookies()
closure.delegate = cookies
closure()
}
void body(Map<String, Object> body) {
this.body = new Body(convertObjectsToDslProperties(body))
}
@@ -184,6 +192,17 @@ class Response extends Common {
}
}
@CompileStatic
@EqualsAndHashCode(includeFields = true)
@ToString(includePackage = false)
private class ResponseCookies extends Cookies {
@Override
DslProperty matching(String value) {
return $(p(regex("${RegexpUtils.escapeSpecialRegexWithSingleEscape(value)}.*")), c(value))
}
}
@CompileStatic
@EqualsAndHashCode(includeFields = true)
@ToString(includePackage = false)

View File

@@ -0,0 +1,28 @@
package org.springframework.cloud.contract.spec.internal
import spock.lang.Specification
/**
* @author Marcin Grzejszczak
*/
class CookiesSpec extends Specification {
Cookies cookies = cookie()
def "should convert cookies to a stub side map"() {
expect:
cookies.asStubSideMap() == [foo: "client", bar: "client"]
}
def "should convert cookies to a test side map"() {
expect:
cookies.asTestSideMap() == [foo: "server", bar: "server"]
}
private Cookies cookie() {
Cookies cookies = new Cookies()
cookies.cookie("foo", new DslProperty("client", "server"))
cookies.cookie(["bar": new DslProperty<>("client", "server")])
return cookies
}
}

View File

@@ -1,6 +0,0 @@
#Mon Jul 31 11:13:36 CEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.0.2-bin.zip

View File

@@ -12,7 +12,7 @@
<snapshotVersion>
<extension>jar</extension>
<value>0.0.1-SNAPSHOT</value>
<updated>20160916125313</updated>
<updated>20170916125313</updated>
</snapshotVersion>
<snapshotVersion>
<extension>pom</extension>

View File

@@ -20,6 +20,7 @@ import groovy.json.StringEscapeUtils
import groovy.transform.PackageScope
import groovy.transform.TypeChecked
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.spec.internal.Cookie
import org.springframework.cloud.contract.spec.internal.ExecutionProperty
import org.springframework.cloud.contract.spec.internal.Header
import org.springframework.cloud.contract.spec.internal.Input
@@ -113,6 +114,19 @@ class JUnitMessagingMethodBodyBuilder extends MessagingMethodBodyBuilder {
blockBuilder.addLine("${exec.insertValue("response.getHeader(\"$property\").toString()")};")
}
@Override
protected void processCookieElement(BlockBuilder blockBuilder, String key, String value) {
}
@Override
protected void processCookieElement(BlockBuilder blockBuilder, String key, GString value) {
}
@Override
protected void processCookieElement(BlockBuilder blockBuilder, String key, Pattern pattern) {
}
@Override
protected void validateResponseCodeBlock(BlockBuilder bb) {
@@ -129,6 +143,10 @@ class JUnitMessagingMethodBodyBuilder extends MessagingMethodBodyBuilder {
}
}
@Override
protected void validateResponseCookiesBlock(BlockBuilder bb) {
}
private String sentToValue(Object sentTo) {
if (sentTo instanceof ExecutionProperty) {
return ((ExecutionProperty) sentTo).executionCommand
@@ -192,6 +210,11 @@ class JUnitMessagingMethodBodyBuilder extends MessagingMethodBodyBuilder {
return ".header(${getTestSideValue(header.name)}, ${getTestSideValue(header.serverValue)})"
}
@Override
protected String getCookieString(Cookie cookie) {
return ""
}
@Override
protected String getBodyString(Object body) {
return ""

View File

@@ -20,6 +20,7 @@ import groovy.json.StringEscapeUtils
import groovy.transform.PackageScope
import groovy.transform.TypeChecked
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.spec.internal.Cookie
import org.springframework.cloud.contract.spec.internal.ExecutionProperty
import org.springframework.cloud.contract.spec.internal.Header
import org.springframework.cloud.contract.spec.internal.NamedProperty
@@ -147,6 +148,11 @@ abstract class JUnitMethodBodyBuilder extends RequestProcessingMethodBodyBuilder
return ".header(${getTestSideValue(header.name)}, ${getTestSideValue(header.serverValue)})"
}
@Override
protected String getCookieString(Cookie cookie) {
return ".cookie(${getTestSideValue(cookie.key)}, ${getTestSideValue(cookie.serverValue)})"
}
@Override
protected String getBodyString(Object body) {
String value
@@ -177,6 +183,15 @@ abstract class JUnitMethodBodyBuilder extends RequestProcessingMethodBodyBuilder
return buildEscapedMatchesMethod(headerValue) + ";"
}
protected String createCookieComparison(Object cookieValue) {
String escapedCookie = convertUnicodeEscapesIfRequired("$cookieValue")
return "isEqualTo(\"$escapedCookie\");"
}
protected String createCookieComparison(Pattern cookieValue) {
return buildEscapedMatchesMethod(cookieValue) + ";"
}
private String buildEscapedMatchesMethod(Pattern escapedValue) {
String escapedHeader = convertUnicodeEscapesIfRequired("$escapedValue")
return createMatchesMethod(escapedHeader)

View File

@@ -19,6 +19,7 @@ package org.springframework.cloud.contract.verifier.builder
import groovy.transform.PackageScope
import groovy.transform.TypeChecked
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.spec.internal.Cookie
import org.springframework.cloud.contract.spec.internal.DslProperty
import org.springframework.cloud.contract.spec.internal.Header
import org.springframework.cloud.contract.spec.internal.NotToEscapePattern
@@ -65,6 +66,7 @@ class JaxRsClientJUnitMethodBodyBuilder extends JUnitMethodBodyBuilder {
appendUrlPathAndQueryParameters(bb)
appendRequestWithRequiredResponseContentType(bb)
appendHeaders(bb)
appendCookies(bb)
appendMethodAndBody(bb)
bb.addAtTheEnd(JUNIT.lineSuffix)
@@ -123,6 +125,16 @@ class JaxRsClientJUnitMethodBodyBuilder extends JUnitMethodBodyBuilder {
}
}
protected appendCookies(BlockBuilder bb) {
request.cookies?.executeForEachCookie { Cookie cookie ->
if (cookieOfAbsentType(cookie)) {
return
}
bb.addLine(".cookie(\"${cookie.key}\", \"${cookie.serverValue}\")")
}
}
protected void appendRequestWithRequiredResponseContentType(BlockBuilder bb) {
String acceptHeader = getHeader("Accept")
if (acceptHeader) {
@@ -146,6 +158,15 @@ class JaxRsClientJUnitMethodBodyBuilder extends JUnitMethodBodyBuilder {
}
}
@Override
protected void validateResponseCookiesBlock(BlockBuilder bb) {
response.cookies?.executeForEachCookie { Cookie cookie ->
processCookieElement(bb, cookie.key, cookie.serverValue instanceof NotToEscapePattern ?
cookie.serverValue :
MapConverter.getTestSideValues(cookie.serverValue))
}
}
protected String getHeader(String name) {
return request.headers?.entries.find { it.name == name }?.serverValue
}
@@ -185,4 +206,16 @@ class JaxRsClientJUnitMethodBodyBuilder extends JUnitMethodBodyBuilder {
blockBuilder.addLine("${exec.insertValue("response.getHeaderString(\"$property\")")};")
}
@Override
protected void processCookieElement(BlockBuilder blockBuilder, String key, Pattern pattern) {
blockBuilder.addLine("assertThat(response.getCookies().get(\"$key\")).isNotNull();")
blockBuilder.addLine("assertThat(response.getCookies().get(\"$key\").getValue()).${createCookieComparison(pattern)}")
}
@Override
protected void processCookieElement(BlockBuilder blockBuilder, String key, String value) {
blockBuilder.addLine("assertThat(response.getCookies().get(\"$key\")).isNotNull();")
blockBuilder.addLine("assertThat(response.getCookies().get(\"$key\").getValue()).${createCookieComparison(value)}")
}
}

View File

@@ -19,6 +19,7 @@ package org.springframework.cloud.contract.verifier.builder
import groovy.transform.PackageScope
import groovy.transform.TypeChecked
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.spec.internal.Cookie
import org.springframework.cloud.contract.spec.internal.DslProperty
import org.springframework.cloud.contract.spec.internal.Header
import org.springframework.cloud.contract.spec.internal.NotToEscapePattern
@@ -62,6 +63,7 @@ class JaxRsClientSpockMethodRequestProcessingBodyBuilder extends SpockMethodRequ
appendUrlPathAndQueryParameters(bb)
appendRequestWithRequiredResponseContentType(bb)
appendHeaders(bb)
appendCookies(bb)
appendMethodAndBody(bb)
bb.unindent()
@@ -128,6 +130,16 @@ class JaxRsClientSpockMethodRequestProcessingBodyBuilder extends SpockMethodRequ
}
}
protected appendCookies(BlockBuilder bb) {
request.cookies?.executeForEachCookie { Cookie cookie ->
if (cookieOfAbsentType(cookie)) {
return
}
bb.addLine(".cookie('${cookie.key}', '${cookie.serverValue}')")
}
}
protected String getHeader(String name) {
return request.headers?.entries?.find { it.name == name }?.serverValue
}
@@ -146,6 +158,15 @@ class JaxRsClientSpockMethodRequestProcessingBodyBuilder extends SpockMethodRequ
}
}
@Override
protected void validateResponseCookiesBlock(BlockBuilder bb) {
response.cookies?.executeForEachCookie { Cookie cookie ->
processCookieElement(bb, cookie.key, cookie.serverValue instanceof NotToEscapePattern ?
cookie.serverValue :
MapConverter.getTestSideValues(cookie.serverValue))
}
}
@Override
protected String getResponseAsString() {
return 'responseAsString'
@@ -181,6 +202,18 @@ class JaxRsClientSpockMethodRequestProcessingBodyBuilder extends SpockMethodRequ
blockBuilder.addLine("response.getHeaderString('$property') ${convertHeaderComparison(value)}")
}
@Override
protected void processCookieElement(BlockBuilder blockBuilder, String key, Pattern pattern) {
blockBuilder.addLine("response.getCookies().get('$key') != null")
blockBuilder.addLine("response.getCookies().get('$key').getValue() ${convertCookieComparison(pattern)}")
}
@Override
protected void processCookieElement(BlockBuilder blockBuilder, String key, String value) {
blockBuilder.addLine("response.getCookies().get('$key') != null")
blockBuilder.addLine("response.getCookies().get('$key').getValue() ${convertCookieComparison(value)}")
}
@Override
protected String postProcessJsonPathCall(String jsonPath) {
if (templateProcessor.containsTemplateEntry(jsonPath)) {

View File

@@ -16,6 +16,8 @@
package org.springframework.cloud.contract.verifier.builder
import org.springframework.cloud.contract.spec.internal.Cookie
import java.util.regex.Pattern
import com.jayway.jsonpath.DocumentContext
@@ -100,6 +102,11 @@ abstract class MethodBodyBuilder {
*/
protected abstract void validateResponseHeadersBlock(BlockBuilder bb)
/**
* Builds the response cookies validation code block
*/
protected abstract void validateResponseCookiesBlock(BlockBuilder bb)
/**
* Builds the code that returns response in the string format
*/
@@ -174,6 +181,21 @@ abstract class MethodBodyBuilder {
*/
protected abstract void processHeaderElement(BlockBuilder blockBuilder, String property, Number value)
/**
* Appends to the {@link BlockBuilder} the assertion for the given cookie path
*/
protected abstract void processCookieElement(BlockBuilder blockBuilder, String key, Pattern pattern)
/**
* Appends to the {@link BlockBuilder} the assertion for the given cookie path
*/
protected abstract void processCookieElement(BlockBuilder blockBuilder, String key, String value)
/**
* Appends to the {@link BlockBuilder} the assertion for the given cookie path
*/
protected abstract void processCookieElement(BlockBuilder blockBuilder, String key, GString value)
/**
* Appends to the {@link BlockBuilder} the code to retrieve a value for a property
* from the list with the given index
@@ -203,6 +225,11 @@ abstract class MethodBodyBuilder {
*/
protected abstract String getHeaderString(Header header)
/**
* Builds the code to append a cookie to the request / message
*/
protected abstract String getCookieString(Cookie cookie)
/**
* Builds the code to append body to the request / message
*/
@@ -611,6 +638,12 @@ abstract class MethodBodyBuilder {
protected void processHeaderElement(BlockBuilder blockBuilder, String property, Object value) {
}
/**
* Appends to the {@link BlockBuilder} the assertion for the given cookie
*/
protected void processCookieElement(BlockBuilder blockBuilder, String key, Object value) {
}
/**
* Appends to the {@link BlockBuilder} the assertion for the given body element
*/

View File

@@ -19,6 +19,7 @@ package org.springframework.cloud.contract.verifier.builder
import groovy.transform.PackageScope
import groovy.transform.TypeChecked
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.spec.internal.Cookie
import org.springframework.cloud.contract.spec.internal.ExecutionProperty
import org.springframework.cloud.contract.spec.internal.Header
import org.springframework.cloud.contract.spec.internal.NotToEscapePattern
@@ -53,6 +54,15 @@ class MockMvcSpockMethodRequestProcessingBodyBuilder extends SpockMethodRequestP
}
}
@Override
protected void validateResponseCookiesBlock(BlockBuilder bb) {
response.cookies?.executeForEachCookie { Cookie cookie ->
processCookieElement(bb, cookie.key, cookie.serverValue instanceof NotToEscapePattern ?
cookie.serverValue :
MapConverter.getTestSideValues(cookie.serverValue))
}
}
@Override
protected String getResponseAsString() {
return 'response.body.asString()'
@@ -69,6 +79,16 @@ class MockMvcSpockMethodRequestProcessingBodyBuilder extends SpockMethodRequestP
}
}
@Override
protected void processCookieElement(BlockBuilder blockBuilder, String key, Object value) {
if (value instanceof NotToEscapePattern) {
blockBuilder.addLine("response.cookie('$key') " +
"${patternComparison(((NotToEscapePattern) value).serverValue.pattern().replace("\\", "\\\\"))}")
} else {
processCookieElement(blockBuilder, key, value.toString())
}
}
@Override
protected void processHeaderElement(BlockBuilder blockBuilder, String property, Number number) {
blockBuilder.addLine("response.header('$property') == ${number}")
@@ -89,6 +109,18 @@ class MockMvcSpockMethodRequestProcessingBodyBuilder extends SpockMethodRequestP
blockBuilder.addLine("response.header('$property') ${convertHeaderComparison(value)}")
}
@Override
protected void processCookieElement(BlockBuilder blockBuilder, String key, Pattern pattern) {
blockBuilder.addLine("response.cookie('$key') != null")
blockBuilder.addLine("response.cookie('$key') ${convertCookieComparison(pattern)}")
}
@Override
protected void processCookieElement(BlockBuilder blockBuilder, String key, String value) {
blockBuilder.addLine("response.cookie('$key') != null")
blockBuilder.addLine("response.cookie('$key') ${convertCookieComparison(value)}")
}
// #273 - should escape $ for Groovy since it will try to make it a GString
@Override
protected String postProcessJsonPathCall(String jsonPath) {

View File

@@ -22,6 +22,7 @@ import groovy.transform.TypeChecked
import groovy.transform.TypeCheckingMode
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.spec.internal.BodyMatchers
import org.springframework.cloud.contract.spec.internal.Cookie
import org.springframework.cloud.contract.spec.internal.ExecutionProperty
import org.springframework.cloud.contract.spec.internal.Header
import org.springframework.cloud.contract.spec.internal.MatchingStrategy
@@ -101,6 +102,14 @@ abstract class RequestProcessingMethodBodyBuilder extends MethodBodyBuilder {
}
bb.addLine(getHeaderString(header))
}
request.cookies?.executeForEachCookie { Cookie cookie ->
if (cookieOfAbsentType(cookie)) {
return
}
bb.addLine(getCookieString(cookie))
}
if (request.body) {
Object body = request.body?.serverValue instanceof ExecutionProperty ?
request.body?.serverValue : bodyAsString
@@ -116,6 +125,11 @@ abstract class RequestProcessingMethodBodyBuilder extends MethodBodyBuilder {
((MatchingStrategy) header.serverValue).type == MatchingStrategy.Type.ABSENT
}
protected boolean cookieOfAbsentType(Cookie cookie) {
return cookie.serverValue instanceof MatchingStrategy &&
((MatchingStrategy) cookie.serverValue).type == MatchingStrategy.Type.ABSENT
}
@Override
protected void when(BlockBuilder bb) {
bb.addLine(getInputString(request))
@@ -169,6 +183,9 @@ abstract class RequestProcessingMethodBodyBuilder extends MethodBodyBuilder {
if (response.headers) {
validateResponseHeadersBlock(bb)
}
if (response.cookies) {
validateResponseCookiesBlock(bb)
}
if (response.body) {
bb.endBlock()
bb.addLine(addCommentSignIfRequired('and:')).startBlock()
@@ -189,6 +206,12 @@ abstract class RequestProcessingMethodBodyBuilder extends MethodBodyBuilder {
processHeaderElement(blockBuilder, property, gstringValue)
}
@Override
protected void processCookieElement(BlockBuilder blockBuilder, String key, GString value) {
String gStringValue = ContentUtils.extractValueForGString(value, ContentUtils.GET_TEST_SIDE).toString()
processCookieElement(blockBuilder, key, gStringValue)
}
@Override
protected ContentType getResponseContentType() {
ContentType contentType = recognizeContentTypeFromHeader(response.headers)

View File

@@ -19,6 +19,7 @@ package org.springframework.cloud.contract.verifier.builder
import groovy.transform.PackageScope
import groovy.transform.TypeChecked
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.spec.internal.Cookie
import org.springframework.cloud.contract.spec.internal.ExecutionProperty
import org.springframework.cloud.contract.spec.internal.Header
import org.springframework.cloud.contract.spec.internal.NotToEscapePattern
@@ -55,6 +56,15 @@ class RestAssuredJUnitMethodBodyBuilder extends JUnitMethodBodyBuilder {
}
}
@Override
protected void validateResponseCookiesBlock(BlockBuilder bb) {
response.cookies?.executeForEachCookie { Cookie cookie ->
processCookieElement(bb, cookie.key, cookie.serverValue instanceof NotToEscapePattern ?
cookie.serverValue :
MapConverter.getTestSideValues(cookie.serverValue))
}
}
@Override
protected String getResponseBodyPropertyComparisonString(String property, Object value) {
return null
@@ -96,4 +106,15 @@ class RestAssuredJUnitMethodBodyBuilder extends JUnitMethodBodyBuilder {
blockBuilder.addLine("${exec.insertValue("response.header(\"$property\")")};")
}
@Override
protected void processCookieElement(BlockBuilder blockBuilder, String key, Pattern pattern) {
blockBuilder.addLine("assertThat(response.getCookie(\"$key\")).isNotNull();")
blockBuilder.addLine("assertThat(response.getCookie(\"$key\")).${createCookieComparison(pattern)}")
}
@Override
protected void processCookieElement(BlockBuilder blockBuilder, String key, String value) {
blockBuilder.addLine("assertThat(response.getCookie(\"$key\")).isNotNull();")
blockBuilder.addLine("assertThat(response.getCookie(\"$key\")).${createCookieComparison(value)}")
}
}

View File

@@ -20,6 +20,7 @@ import groovy.json.StringEscapeUtils
import groovy.transform.PackageScope
import groovy.transform.TypeChecked
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.spec.internal.Cookie
import org.springframework.cloud.contract.spec.internal.ExecutionProperty
import org.springframework.cloud.contract.spec.internal.Header
import org.springframework.cloud.contract.spec.internal.Input
@@ -93,6 +94,18 @@ class SpockMessagingMethodBodyBuilder extends MessagingMethodBodyBuilder {
blockBuilder.addLine("response.getHeader('$property')?.toString() ${convertHeaderComparison(value)}")
}
@Override
protected void processCookieElement(BlockBuilder blockBuilder, String key, Pattern pattern) {
}
@Override
protected void processCookieElement(BlockBuilder blockBuilder, String key, String value) {
}
@Override
protected void processCookieElement(BlockBuilder blockBuilder, String key, GString value) {
}
@Override
protected void validateResponseCodeBlock(BlockBuilder bb) {
if (outputMessage) {
@@ -119,6 +132,10 @@ class SpockMessagingMethodBodyBuilder extends MessagingMethodBodyBuilder {
}
}
@Override
protected void validateResponseCookiesBlock(BlockBuilder bb) {
}
@Override
protected String getResponseAsString() {
return 'contractVerifierObjectMapper.writeValueAsString(response.payload)'
@@ -184,6 +201,11 @@ class SpockMessagingMethodBodyBuilder extends MessagingMethodBodyBuilder {
return "${getTestSideValue(header.name)}: ${getTestSideValue(header.serverValue)}"
}
@Override
protected String getCookieString(Cookie cookie) {
return ''
}
@Override
protected String getBodyString(Object body) {
return ''

View File

@@ -20,6 +20,7 @@ import groovy.json.StringEscapeUtils
import groovy.transform.PackageScope
import groovy.transform.TypeChecked
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.spec.internal.Cookie
import org.springframework.cloud.contract.spec.internal.Header
import org.springframework.cloud.contract.spec.internal.NamedProperty
import org.springframework.cloud.contract.spec.internal.Request
@@ -122,6 +123,11 @@ abstract class SpockMethodRequestProcessingBodyBuilder extends RequestProcessing
return ".header(${getTestSideValue(header.name)}, ${getTestSideValue(header.serverValue)})"
}
@Override
protected String getCookieString(Cookie cookie) {
return ".cookie(${getTestSideValue(cookie.key)}, ${getTestSideValue(cookie.serverValue)})"
}
@Override
protected String getBodyString(Object body) {
String value
@@ -149,6 +155,12 @@ abstract class SpockMethodRequestProcessingBodyBuilder extends RequestProcessing
processHeaderElement(blockBuilder, property, gstringValue)
}
@Override
protected void processCookieElement(BlockBuilder blockBuilder, String key, GString value) {
String gStringValue = ContentUtils.extractValueForGString(value, ContentUtils.GET_TEST_SIDE).toString()
processCookieElement(blockBuilder, key, gStringValue)
}
protected String convertHeaderComparison(String headerValue) {
return " == '$headerValue'"
}
@@ -157,6 +169,14 @@ abstract class SpockMethodRequestProcessingBodyBuilder extends RequestProcessing
return patternComparison(headerValue)
}
protected String convertCookieComparison(String cookieValue) {
return "== '$cookieValue'"
}
protected String convertCookieComparison(Pattern cookieValue) {
return patternComparison(cookieValue)
}
protected String patternComparison(Pattern pattern) {
return patternComparison(pattern.toString())
}

View File

@@ -44,6 +44,7 @@ class YamlContract {
public String urlPath
public Map<String, Object> queryParameters = [:]
public Map<String, Object> headers = [:]
public Map<String, Object> cookies = [:]
public Object body
public String bodyFromFile
public StubMatchers matchers = new StubMatchers()
@@ -67,6 +68,7 @@ class YamlContract {
static class StubMatchers {
public List<BodyStubMatcher> body = []
public List<KeyValueMatcher> headers = []
public List<KeyValueMatcher> cookies = []
public MultipartStubMatcher multipart
}
@@ -122,6 +124,14 @@ class YamlContract {
public PredefinedRegex predefined
}
@CompileStatic
static class TestCookieMatcher {
public String key
public String regex
public String command
public PredefinedRegex predefined
}
@CompileStatic
static enum PredefinedRegex {
only_alpha_unicode, number, any_double, any_boolean, ip_address, hostname,
@@ -143,6 +153,7 @@ class YamlContract {
static class Response {
public int status
public Map<String, Object> headers = [:]
public Map<String, Object> cookies = [:]
public Object body
public String bodyFromFile
public TestMatchers matchers = new TestMatchers()
@@ -153,6 +164,7 @@ class YamlContract {
static class TestMatchers {
public List<BodyTestMatcher> body = []
public List<TestHeaderMatcher> headers = []
public List<TestCookieMatcher> cookies = []
}
@CompileStatic

View File

@@ -26,6 +26,7 @@ import org.yaml.snakeyaml.Yaml
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.spec.ContractConverter
import org.springframework.cloud.contract.spec.internal.BodyMatcher
import org.springframework.cloud.contract.spec.internal.Cookies
import org.springframework.cloud.contract.spec.internal.DslProperty
import org.springframework.cloud.contract.spec.internal.ExecutionProperty
import org.springframework.cloud.contract.spec.internal.Headers
@@ -45,6 +46,7 @@ import org.springframework.cloud.contract.verifier.converter.YamlContract.KeyVal
import org.springframework.cloud.contract.verifier.converter.YamlContract.StubMatcherType
import org.springframework.cloud.contract.verifier.converter.YamlContract.StubMatchers
import org.springframework.cloud.contract.verifier.converter.YamlContract.TestHeaderMatcher
import org.springframework.cloud.contract.verifier.converter.YamlContract.TestCookieMatcher
import org.springframework.cloud.contract.verifier.converter.YamlContract.TestMatcherType
import org.springframework.cloud.contract.verifier.util.MapConverter
@@ -133,6 +135,16 @@ class YamlContractConverter implements ContractConverter<List<YamlContract>> {
}
}
}
if (yamlContract.request?.cookies) {
cookies {
yamlContract.request?.cookies?.each { String key, Object value ->
KeyValueMatcher matcher = yamlContract.request.matchers.cookies.find { it.key == key }
Object clientValue = clientValue(value, matcher, key)
cookie(key, new DslProperty(clientValue, value))
}
}
}
if (yamlContract.request.body) body(yamlContract.request.body)
if (yamlContract.request.bodyFromFile) body(file(yamlContract.request.bodyFromFile))
if (yamlContract.request.multipart) {
@@ -212,6 +224,16 @@ class YamlContractConverter implements ContractConverter<List<YamlContract>> {
}
}
}
if (yamlContract.response?.cookies) {
cookies {
yamlContract.response?.cookies?.each { String key, Object value ->
TestCookieMatcher matcher = yamlContract.response.matchers.cookies.find { it.key == key }
Object serverValue = serverCookieValue(value, matcher, key)
cookie(key, new DslProperty(value, serverValue))
}
}
}
if (yamlContract.response.body) body(yamlContract.response.body)
if (yamlContract.response.bodyFromFile) body(file(yamlContract.response.bodyFromFile))
if (yamlContract.response.async) async()
@@ -392,6 +414,20 @@ class YamlContractConverter implements ContractConverter<List<YamlContract>> {
return serverValue
}
protected Object serverCookieValue(Object value, TestCookieMatcher matcher, String key) {
Object serverValue = value
if (matcher?.regex) {
serverValue = Pattern.compile(matcher.regex)
Pattern pattern = (Pattern) serverValue
assertPatternMatched(pattern, value, key)
} else if (matcher?.predefined) {
Pattern pattern = predefinedToPattern(matcher.predefined)
serverValue = pattern
assertPatternMatched(pattern, value, key)
}
return serverValue
}
protected Object clientValue(Object value, KeyValueMatcher matcher, String key) {
Object clientValue = value
if (matcher?.regex) {
@@ -473,6 +509,7 @@ class YamlContractConverter implements ContractConverter<List<YamlContract>> {
method = contract?.request?.method?.clientValue
url = contract?.request?.url?.clientValue
headers = (contract?.request?.headers as Headers)?.asTestSideMap()
cookies = (contract?.request?.cookies as Cookies)?.asTestSideMap()
body = MapConverter.getTestSideValues(contract?.request?.body)
matchers = new StubMatchers()
contract?.request?.bodyMatchers?.jsonPathMatchers()?.each { BodyMatcher matcher ->
@@ -487,6 +524,7 @@ class YamlContractConverter implements ContractConverter<List<YamlContract>> {
yamlContract.response.with {
status = contract?.response?.status?.clientValue as Integer
headers = (contract?.response?.headers as Headers)?.asStubSideMap()
cookies = (contract?.response?.cookies as Cookies)?.asStubSideMap()
body = MapConverter.getStubSideValues(contract?.response?.body)
contract?.response?.bodyMatchers?.jsonPathMatchers()?.each { BodyMatcher matcher ->
matchers.body << new BodyTestMatcher(

View File

@@ -24,6 +24,7 @@ import com.github.tomakehurst.wiremock.matching.StringValuePattern
import com.github.tomakehurst.wiremock.matching.UrlPattern
import groovy.json.JsonOutput
import groovy.json.StringEscapeUtils
import groovy.transform.CompileDynamic
import groovy.transform.PackageScope
import groovy.transform.TypeChecked
import groovy.transform.TypeCheckingMode
@@ -75,6 +76,7 @@ class WireMockRequestStubStrategy extends BaseWireMockStubStrategy {
}
RequestPatternBuilder requestPatternBuilder = appendMethodAndUrl()
ContentType contentType = tryToGetContentType(request?.body?.clientValue, request?.headers)
appendCookies(requestPatternBuilder, contentType)
appendHeaders(requestPatternBuilder, contentType)
appendQueryParameters(requestPatternBuilder, contentType)
appendBody(requestPatternBuilder, contentType)
@@ -152,6 +154,15 @@ class WireMockRequestStubStrategy extends BaseWireMockStubStrategy {
}
}
private void appendCookies(RequestPatternBuilder requestPattern, ContentType contentType) {
if(!request.cookies) {
return
}
request.cookies.entries.each {
requestPattern.withCookie(it.key, convertToValuePattern(it.clientValue, contentType))
}
}
private UrlPattern urlPattern() {
Object urlPath = urlPathOrUrlIfQueryPresent()
if (urlPath) {
@@ -314,6 +325,7 @@ class WireMockRequestStubStrategy extends BaseWireMockStubStrategy {
return containsPattern(map.entrySet())
}
@CompileDynamic
private boolean containsPattern(Collection collection) {
return collection.collect(this.&containsPattern).inject('') { a, b -> a || b }
}

View File

@@ -153,6 +153,28 @@ class ContractHttpDocsSpec extends Specification {
}
// end::headers[]
org.springframework.cloud.contract.spec.Contract cookies =
// tag::cookies[]
org.springframework.cloud.contract.spec.Contract.make {
request {
//...
// Each Cookies is added in form `'Cookie-Key' : 'Cookie-Value'`.
// there are also some helper methods
cookies {
cookie 'key': 'value'
cookie('another_key', 'another_value')
}
//...
}
response {
//...
}
}
// end::cookies[]
org.springframework.cloud.contract.spec.Contract body =
// tag::body[]
org.springframework.cloud.contract.spec.Contract.make {

View File

@@ -34,6 +34,71 @@ class JaxRsClientMethodBuilderSpec extends Specification implements WireMockStub
@Shared ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties(assertJsonSize: true)
@Shared
// tag::contract_with_cookies[]
Contract contractDslWithCookiesValue = Contract.make {
request {
method "GET"
url "/foo"
headers {
header 'Accept': 'application/json'
}
cookies {
cookie 'cookie-key': 'cookie-value'
}
}
response {
status 200
headers {
header 'Content-Type': 'application/json'
}
cookies {
cookie 'cookie-key': 'new-cookie-value'
}
body([status: 'OK'])
}
}
// end::contract_with_cookies[]
@Shared
Contract contractDslWithCookiesPattern = Contract.make {
request {
method "GET"
url "/foo"
headers {
header 'Accept': 'application/json'
}
cookies {
cookie 'cookie-key': regex('[A-Za-z]+')
}
}
response {
status 200
headers {
header 'Content-Type': 'application/json'
}
cookies {
cookie 'cookie-key': regex('[A-Za-z]+')
}
body([status: 'OK'])
}
}
@Shared
Contract contractDslWithAbsentCookies = Contract.make {
request {
method "GET"
url "/foo"
cookies {
cookie 'cookie-key': absent()
}
}
response {
status 200
body([status: 'OK'])
}
}
def "should generate assertions for simple response body with #methodBuilderName"() {
given:
Contract contractDsl = Contract.make {
@@ -1214,4 +1279,90 @@ DATA
methodBuilderName | methodBuilder
"JaxRsClientJUnitMethodBodyBuilder" | { org.springframework.cloud.contract.spec.Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) }
}
def "should generate test for cookies with string value in JAX-RS JUnit test"() {
given:
MethodBodyBuilder builder = new JaxRsClientJUnitMethodBodyBuilder(contractDslWithCookiesValue, properties)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
def test = blockBuilder.toString()
then:
test.contains('''.cookie("cookie-key", "cookie-value")''')
test.contains('''assertThat(response.getCookies().get("cookie-key")).isNotNull();''')
test.contains('''assertThat(response.getCookies().get("cookie-key").getValue()).isEqualTo("new-cookie-value");''')
and:
SyntaxChecker.tryToCompile("JaxRsClientJUnitMethodBodyBuilder", blockBuilder.toString())
}
def "should generate test for cookies with pattern in JAX-RS JUnit test"() {
given:
MethodBodyBuilder builder = new JaxRsClientJUnitMethodBodyBuilder(contractDslWithCookiesPattern, properties)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
def test = blockBuilder.toString()
then:
test.contains('''.cookie("cookie-key", "[A-Za-z]+")''')
test.contains('''assertThat(response.getCookies().get("cookie-key")).isNotNull();''')
test.contains('''assertThat(response.getCookies().get("cookie-key").getValue()).matches("[A-Za-z]+");''')
and:
SyntaxChecker.tryToCompile("JaxRsClientJUnitMethodBodyBuilder", blockBuilder.toString())
}
def "should not generate cookie assertions with absent value in JAX-RS JUnit test"() {
given:
MethodBodyBuilder builder = new JaxRsClientJUnitMethodBodyBuilder(contractDslWithAbsentCookies, properties)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
def test = blockBuilder.toString()
then:
!test.contains("cookie")
and:
SyntaxChecker.tryToCompile("JaxRsClientJunitMethodBodyBuilder", blockBuilder.toString())
}
def "should generate test for cookies with string value in JAX-RS Spock test"() {
given:
MethodBodyBuilder builder = new JaxRsClientSpockMethodRequestProcessingBodyBuilder(contractDslWithCookiesValue, properties)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
def test = blockBuilder.toString()
then:
test.contains('''.cookie('cookie-key', 'cookie-value')''')
test.contains('''response.getCookies().get('cookie-key') != null''')
test.contains("response.getCookies().get('cookie-key').getValue() == 'new-cookie-value'")
and:
SyntaxChecker.tryToCompile("JaxRsClientSpockMethodRequestProcessingBodyBuilder", blockBuilder.toString())
}
def "should generate test for cookies with pattern in JAX-RS Spock test"() {
given:
MethodBodyBuilder builder = new JaxRsClientSpockMethodRequestProcessingBodyBuilder(contractDslWithCookiesPattern, properties)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
def test = blockBuilder.toString()
then:
test.contains('''.cookie('cookie-key', '[A-Za-z]+')''')
test.contains('''response.getCookies().get('cookie-key') != null''')
test.contains('''response.getCookies().get('cookie-key').getValue() ==~ java.util.regex.Pattern.compile('[A-Za-z]+')''')
and:
SyntaxChecker.tryToCompile("JaxRsClientSpockMethodRequestProcessingBodyBuilder", blockBuilder.toString())
}
def "should not generate cookie assertions with absent value in JAX-RS Spock test"() {
given:
MethodBodyBuilder builder = new JaxRsClientSpockMethodRequestProcessingBodyBuilder(contractDslWithAbsentCookies, properties)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
def test = blockBuilder.toString()
then:
!test.contains("cookie")
and:
SyntaxChecker.tryToCompile("JaxRsClientSpockMethodRequestProcessingBodyBuilder", blockBuilder.toString())
}
}

View File

@@ -42,6 +42,69 @@ class MockMvcMethodBodyBuilderSpec extends Specification implements WireMockStub
assertJsonSize: true
)
@Shared
Contract contractDslWithCookiesValue = Contract.make {
request {
method "GET"
url "/foo"
headers {
header 'Accept': 'application/json'
}
cookies {
cookie 'cookie-key': 'cookie-value'
}
}
response {
status 200
headers {
header 'Content-Type': 'application/json'
}
cookies {
cookie 'cookie-key': 'new-cookie-value'
}
body([status: 'OK'])
}
}
@Shared
Contract contractDslWithCookiesPattern = Contract.make {
request {
method "GET"
url "/foo"
headers {
header 'Accept': 'application/json'
}
cookies {
cookie 'cookie-key': regex('[A-Za-z]+')
}
}
response {
status 200
headers {
header 'Content-Type': 'application/json'
}
cookies {
cookie 'cookie-key': regex('[A-Za-z]+')
}
body([status: 'OK'])
}
}
@Shared
Contract contractDslWithAbsentCookies = Contract.make {
request {
method "GET"
url "/foo"
cookies {
cookie 'cookie-key': absent()
}
}
response {
status 200
body([status: 'OK'])
}
}
@Shared
// tag::contract_with_regex[]
Contract dslWithOptionalsInString = Contract.make {
@@ -2512,4 +2575,91 @@ DocumentContext parsedJson = JsonPath.parse(json);
"JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { Contract dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl, properties) } | { String body -> body.contains("response.getHeaderString('Authorization') == 'foo secret bar'") }
"JaxRsClientJUnitMethodBodyBuilder" | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) } | { String body -> body.contains('assertThat(response.getHeaderString("Authorization")).isEqualTo("foo secret bar");') }
}
def "should generate JUnit assertions with cookies"() {
given:
MethodBodyBuilder builder = new MockMvcJUnitMethodBodyBuilder(contractDslWithCookiesValue, properties)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
def test = blockBuilder.toString()
then:
test.contains('''.cookie("cookie-key", "cookie-value")''')
test.contains('''assertThat(response.getCookie("cookie-key")).isNotNull();''')
test.contains('''assertThat(response.getCookie("cookie-key")).isEqualTo("new-cookie-value");''')
and:
SyntaxChecker.tryToCompile("MockMvcJUnitMethodBodyBuilder", blockBuilder.toString())
}
def "should generate JUnit assertions with cookies pattern"() {
given:
MethodBodyBuilder builder = new MockMvcJUnitMethodBodyBuilder(contractDslWithCookiesPattern, properties)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
def test = blockBuilder.toString()
then:
test.contains('''.cookie("cookie-key", "[A-Za-z]+")''')
test.contains('''assertThat(response.getCookie("cookie-key")).isNotNull();''')
test.contains('''assertThat(response.getCookie("cookie-key")).matches("[A-Za-z]+");''')
and:
SyntaxChecker.tryToCompile("MockMvcJUnitMethodBodyBuilder", blockBuilder.toString())
}
def "should not generate JUnit cookie assertion with absent cookie"() {
given:
MethodBodyBuilder builder = new MockMvcJUnitMethodBodyBuilder(contractDslWithAbsentCookies, properties)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
def test = blockBuilder.toString()
then:
!test.contains("cookie")
and:
SyntaxChecker.tryToCompile("MockMvcJUnitMethodBodyBuilder", blockBuilder.toString())
}
def "should generate spock assertions with cookies"() {
given:
MethodBodyBuilder builder = new MockMvcSpockMethodRequestProcessingBodyBuilder(contractDslWithCookiesValue, properties)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
def test = blockBuilder.toString()
then:
test.contains('''.cookie("cookie-key", "cookie-value")''')
test.contains('''response.cookie('cookie-key') != null''')
test.contains('''response.cookie('cookie-key') == 'new-cookie-value''')
and:
SyntaxChecker.tryToCompile("MockMvcSpockMethodRequestProcessingBodyBuilder", blockBuilder.toString())
}
def "should generate spock assertions with cookies pattern"() {
given:
MethodBodyBuilder builder = new MockMvcSpockMethodRequestProcessingBodyBuilder(contractDslWithCookiesPattern, properties)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
def test = blockBuilder.toString()
then:
test.contains('''.cookie("cookie-key", "[A-Za-z]+")''')
test.contains('''response.cookie('cookie-key') != null''')
test.contains('''response.cookie('cookie-key') ==~ java.util.regex.Pattern.compile('[A-Za-z]+')''')
and:
SyntaxChecker.tryToCompile("MockMvcSpockMethodRequestProcessingBodyBuilder", blockBuilder.toString())
}
def "should not generate spock cookie assertion with absent cookie"() {
given:
MethodBodyBuilder builder = new MockMvcSpockMethodRequestProcessingBodyBuilder(contractDslWithAbsentCookies, properties)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
def test = blockBuilder.toString()
then:
!test.contains("cookie")
and:
SyntaxChecker.tryToCompile("MockMvcSpockMethodRequestProcessingBodyBuilder", blockBuilder.toString())
}
}

View File

@@ -59,8 +59,36 @@ class YamlContractConverterSpec extends Specification {
File ymlMultiple = new File(ymlMultipleFile.toURI())
URL ymlMessagingMatchersFile = YamlContractConverterSpec.getResource("/yml/contract_message_matchers.yml")
File ymlMessagingMatchers = new File(ymlMessagingMatchersFile.toURI())
URL ymlCookiesUrl = YamlContractConverterSpec.getResource("/yml/contract_cookies.yml")
File ymlCookies = new File(ymlCookiesUrl.toURI())
YamlContractConverter converter = new YamlContractConverter()
def "should convert YAML with Cookies to DSL"() {
given:
assert converter.isAccepted(ymlCookies)
when:
Collection<Contract> contracts = converter.convertFrom(ymlCookies)
then:
contracts.size() == 1
Contract contract = contracts.first()
contract.description == "Contract with cookies"
contract.name == "cookies-contract"
contract.priority == 1
contract.ignored == true
contract.request.method.clientValue == "PUT"
contract.request.url.clientValue == "/foo"
contract.request.cookies.entries.find { it.key == "foo" && it.serverValue == "bar" }
contract.request.cookies.entries.find { it.key == "fooRegex" && ((Pattern) it.clientValue).pattern == "reg" && it.serverValue == "reg" }
contract.request.cookies.entries.find { it.key == "fooPredefinedRegex" && ((Pattern) it.clientValue).pattern == "(true|false)" && it.serverValue == true }
and:
contract.response.status.clientValue == 200
contract.response.cookies.entries.find { it.key == "foo" && it.clientValue == "baz" }
contract.response.cookies.entries.find { it.key == "fooRegex" && ((Pattern) it.serverValue).pattern == "[0-9]+" && it.clientValue == 123 }
contract.response.cookies.entries.find { it.key == "source" && ((Pattern) it.serverValue).pattern == "ip_address" && it.clientValue == "ip_address" }
contract.response.cookies.entries.find { it.key == "fooPredefinedRegex" && ((Pattern) it.serverValue).pattern == "(true|false)" && it.clientValue == true }
contract.response.body.clientValue == ["status": "OK"]
}
def "should convert YAML with REST to DSL for [#yamlFile]"() {
given:
assert converter.isAccepted(yamlFile)
@@ -502,6 +530,10 @@ class YamlContractConverterSpec extends Specification {
headers {
header("foo", "bar")
}
cookies {
cookie(foo: value(c("client"), p("server")))
cookie("bar", value(c("client"), p("server")))
}
body([foo: "bar"])
}
response {
@@ -509,6 +541,10 @@ class YamlContractConverterSpec extends Specification {
headers {
header("foo2", "bar")
}
cookies {
cookie(foo: value(c("client"), p("server")))
cookie("bar", value(c("client"), p("server")))
}
body([foo2: "bar"])
}
}]
@@ -520,9 +556,13 @@ class YamlContractConverterSpec extends Specification {
yamlContract.request.url == "/foo"
yamlContract.request.method == "PUT"
yamlContract.request.headers.find { it.key == "foo" && it.value == "bar" }
yamlContract.request.cookies.find { it.key == "bar" && it.value == "server" }
yamlContract.request.cookies.find { it.key == "foo" && it.value == "server" }
yamlContract.request.body == [foo: "bar"]
yamlContract.response.status == 200
yamlContract.response.headers.find { it.key == "foo2" && it.value == "bar" }
yamlContract.response.body == [foo2: "bar"]
yamlContract.response.cookies.find { it.key == "foo" && it.value == "client" }
yamlContract.response.cookies.find { it.key == "bar" && it.value == "client" }
}
}

View File

@@ -1750,6 +1750,9 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie
header(authorization(), "secret")
header(authorization(), "secret2")
}
cookies {
cookie("foo", "bar")
}
body(foo: "bar", baz: 5)
}
response {
@@ -1785,6 +1788,11 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie
"equalTo" : "secret2"
}
},
"cookies" : {
"foo" : {
"equalTo" : "bar"
}
},
"queryParameters" : {
"foo" : {
"equalTo" : "bar2"
@@ -2091,6 +2099,7 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie
RequestEntity.post(URI.create("http://localhost:" + port + "/api/v1/xxxx?foo=bar&foo=bar2"))
.header("Authorization", "secret")
.header("Authorization", "secret2")
.header("Cookie", "foo=bar")
.body("{\"foo\":\"bar\",\"baz\":5}"), String.class)
}
}

View File

@@ -27,6 +27,11 @@ request:
foo: bar
fooReq: baz
#end::headers[]
#tag::cookies[]
cookies:
foo: bar
fooReq: baz
#end::cookies[]
#tag::body[]
body:
foo: bar
@@ -70,4 +75,9 @@ response:
- key: foo2
regex: bar
- key: foo3
command: andMeToo($it)
command: andMeToo($it)
cookies:
- key: foo2
regex: bar
- key: foo3
predefined:

View File

@@ -24,6 +24,11 @@ response:
fooRes: baz
body:
foo2: bar
cookies:
foo: baz
fooRegex: 123
source: ip_address
fooPredefinedRegex: true
matchers:
body:
- path: $.foo2
@@ -31,4 +36,11 @@ response:
value: bar
headers:
- key: foo2
regex: bar
regex: bar
cookies:
- key: fooRegex
regex: "[0-9]+"
- key: source
regex: ip_address
- key: fooPredefinedRegex
predefined: any_boolean

View File

@@ -0,0 +1,34 @@
description: Contract with cookies
name: cookies-contract
priority: 1
ignored: true
request:
method: PUT
url: /foo
cookies:
foo: bar
fooRegex: reg
fooPredefinedRegex: true
matchers:
cookies:
- key: fooRegex
regex: reg
- key: fooPredefinedRegex
predefined: any_boolean
response:
status: 200
cookies:
foo: baz
fooRegex: 123
source: ip_address
fooPredefinedRegex: true
body:
status: OK
matchers:
cookies:
- key: fooRegex
regex: "[0-9]+"
- key: source
regex: ip_address
- key: fooPredefinedRegex
predefined: any_boolean