From 859fff26a8666d5248d143dea17431c27535c1b8 Mon Sep 17 00:00:00 2001 From: Ronald Holshausen Date: Tue, 17 Jan 2023 12:37:11 +1100 Subject: [PATCH] feat: add the remaining status code methods to response DSL builder --- .../pact/consumer/dsl/HttpResponseBuilder.kt | 105 ++++++++++++ .../dsl/HttpRequestBuilderSpec.groovy | 3 +- .../dsl/HttpResponseBuilderSpec.groovy | 152 ++++++------------ 3 files changed, 157 insertions(+), 103 deletions(-) diff --git a/consumer/src/main/kotlin/au/com/dius/pact/consumer/dsl/HttpResponseBuilder.kt b/consumer/src/main/kotlin/au/com/dius/pact/consumer/dsl/HttpResponseBuilder.kt index c5a13a1a0..cee868e7e 100644 --- a/consumer/src/main/kotlin/au/com/dius/pact/consumer/dsl/HttpResponseBuilder.kt +++ b/consumer/src/main/kotlin/au/com/dius/pact/consumer/dsl/HttpResponseBuilder.kt @@ -1,6 +1,11 @@ package au.com.dius.pact.consumer.dsl import au.com.dius.pact.core.model.HttpResponse +import au.com.dius.pact.core.model.matchingrules.HttpStatus +import au.com.dius.pact.core.model.matchingrules.RegexMatcher +import au.com.dius.pact.core.model.matchingrules.RuleLogic +import au.com.dius.pact.core.model.matchingrules.StatusCodeMatcher +import java.util.regex.Pattern /** * Pact HTTP Response builder DSL that supports V4 formatted Pact files @@ -29,6 +34,27 @@ open class HttpResponseBuilder(private val response: HttpResponse): HttpPartBuil return super.headers(values) as HttpResponseBuilder } + /** + * Match a set cookie header + * @param cookie Cookie name to match + * @param regex Regex to match the cookie value with + * @param example Example value + */ + fun matchSetCookie(cookie: String, regex: String, example: String): HttpResponseBuilder { + val headerCategory = response.matchingRules.addCategory("header") + if (headerCategory.numRules("set-cookie") > 0) { + headerCategory.addRule("set-cookie", RegexMatcher(Pattern.quote("$cookie=") + regex)) + } else { + headerCategory.setRule("set-cookie", RegexMatcher(Pattern.quote("$cookie=") + regex), RuleLogic.OR) + } + if (response.headers.containsKey("set-cookie")) { + response.headers["set-cookie"] = response.headers["set-cookie"]!!.plus("$cookie=$example") + } else { + response.headers["set-cookie"] = listOf("$cookie=$example") + } + return this + } + /** * Sets the status code of the response */ @@ -37,6 +63,85 @@ open class HttpResponseBuilder(private val response: HttpResponse): HttpPartBuil return this } + /** + * Match any HTTP Information response status (100-199) + */ + fun informationStatus(): HttpResponseBuilder { + response.matchingRules.addCategory("status").addRule(StatusCodeMatcher(HttpStatus.Information)) + response.status = 100 + return this + } + + /** + * Match any HTTP success response status (200-299) + */ + fun successStatus(): HttpResponseBuilder { + val matcher = StatusCodeMatcher(HttpStatus.Success) + response.matchingRules.addCategory("status").addRule(matcher) + response.status = 200 + return this + } + + /** + * Match any HTTP redirect response status (300-399) + */ + fun redirectStatus(): HttpResponseBuilder { + val matcher = StatusCodeMatcher(HttpStatus.Redirect) + response.matchingRules.addCategory("status").addRule(matcher) + response.status = 300 + return this + } + + /** + * Match any HTTP client error response status (400-499) + */ + fun clientErrorStatus(): HttpResponseBuilder { + val matcher = StatusCodeMatcher(HttpStatus.ClientError) + response.matchingRules.addCategory("status").addRule(matcher) + response.status = 400 + return this + } + + /** + * Match any HTTP server error response status (500-599) + */ + fun serverErrorStatus(): HttpResponseBuilder { + val matcher = StatusCodeMatcher(HttpStatus.ServerError) + response.matchingRules.addCategory("status").addRule(matcher) + response.status = 500 + return this + } + + /** + * Match any HTTP non-error response status (< 400) + */ + fun nonErrorStatus(): HttpResponseBuilder { + val matcher = StatusCodeMatcher(HttpStatus.NonError) + response.matchingRules.addCategory("status").addRule(matcher) + response.status = 200 + return this + } + + /** + * Match any HTTP error response status (>= 400) + */ + fun errorStatus(): HttpResponseBuilder { + val matcher = StatusCodeMatcher(HttpStatus.Error) + response.matchingRules.addCategory("status").addRule(matcher) + response.status = 400 + return this + } + + /** + * Match any HTTP status code in the provided list + */ + fun statusCodes(statusCodes: List): HttpResponseBuilder { + val matcher = StatusCodeMatcher(HttpStatus.StatusCodes, statusCodes) + response.matchingRules.addCategory("status").addRule(matcher) + response.status = statusCodes.first() + return this + } + override fun body(body: String): HttpResponseBuilder { return super.body(body) as HttpResponseBuilder } diff --git a/consumer/src/test/groovy/au/com/dius/pact/consumer/dsl/HttpRequestBuilderSpec.groovy b/consumer/src/test/groovy/au/com/dius/pact/consumer/dsl/HttpRequestBuilderSpec.groovy index f326bc00d..c881d2179 100644 --- a/consumer/src/test/groovy/au/com/dius/pact/consumer/dsl/HttpRequestBuilderSpec.groovy +++ b/consumer/src/test/groovy/au/com/dius/pact/consumer/dsl/HttpRequestBuilderSpec.groovy @@ -220,7 +220,8 @@ class HttpRequestBuilderSpec extends Specification { .build() then: - request.body.valueAsString() == '\n\n' + request.body.valueAsString() == '' + + System.lineSeparator() + '' + System.lineSeparator() request.body.contentType.toString() == 'application/xml' request.headers['content-type'] == ['application/xml'] request.matchingRules.rulesForCategory('body') == new MatchingRuleCategory('body', diff --git a/consumer/src/test/groovy/au/com/dius/pact/consumer/dsl/HttpResponseBuilderSpec.groovy b/consumer/src/test/groovy/au/com/dius/pact/consumer/dsl/HttpResponseBuilderSpec.groovy index e4d2d55f6..372a44baf 100644 --- a/consumer/src/test/groovy/au/com/dius/pact/consumer/dsl/HttpResponseBuilderSpec.groovy +++ b/consumer/src/test/groovy/au/com/dius/pact/consumer/dsl/HttpResponseBuilderSpec.groovy @@ -5,10 +5,13 @@ import au.com.dius.pact.core.model.HttpResponse import au.com.dius.pact.core.model.generators.Category import au.com.dius.pact.core.model.generators.ProviderStateGenerator import au.com.dius.pact.core.model.matchingrules.ContentTypeMatcher +import au.com.dius.pact.core.model.matchingrules.HttpStatus import au.com.dius.pact.core.model.matchingrules.MatchingRuleCategory import au.com.dius.pact.core.model.matchingrules.MatchingRuleGroup import au.com.dius.pact.core.model.matchingrules.NumberTypeMatcher import au.com.dius.pact.core.model.matchingrules.RegexMatcher +import au.com.dius.pact.core.model.matchingrules.StatusCodeMatcher +import au.com.dius.pact.core.model.matchingrules.RuleLogic import kotlin.Pair import spock.lang.Specification @@ -97,6 +100,21 @@ class HttpResponseBuilderSpec extends Specification { response.generators.categoryFor(Category.HEADER) == [A: new ProviderStateGenerator('$a')] } + def 'supports matching set-cookie response headers'() { + when: + def response = builder + .matchSetCookie('A', '\\d+', '100') + .build() + + then: + response.headers == [ + 'set-cookie': ['A=100'] + ] + response.matchingRules.rulesForCategory('header') == new MatchingRuleCategory('header', [ + 'set-cookie': new MatchingRuleGroup([new RegexMatcher('\\QA=\\E\\d+')], RuleLogic.OR) + ]) + } + def 'allows setting the response status'() { when: def response = builder @@ -107,6 +125,36 @@ class HttpResponseBuilderSpec extends Specification { response.status == 204 } + def 'allows setting the response status using common status groups'() { + when: + def response + if (args.empty) { + response = builder."$method"().build() + } else { + response = builder."$method"(args).build() + } + + then: + response.status == status + response.matchingRules.rulesForCategory('status') == new MatchingRuleCategory('status', + [ + '': new MatchingRuleGroup([matchingRule]) + ] + ) + + where: + + method | args | status | matchingRule + 'informationStatus' | [] | 100 | new StatusCodeMatcher(HttpStatus.Information, []) + 'successStatus' | [] | 200 | new StatusCodeMatcher(HttpStatus.Success, []) + 'redirectStatus' | [] | 300 | new StatusCodeMatcher(HttpStatus.Redirect, []) + 'clientErrorStatus' | [] | 400 | new StatusCodeMatcher(HttpStatus.ClientError, []) + 'serverErrorStatus' | [] | 500 | new StatusCodeMatcher(HttpStatus.ServerError, []) + 'nonErrorStatus' | [] | 200 | new StatusCodeMatcher(HttpStatus.NonError, []) + 'errorStatus' | [] | 400 | new StatusCodeMatcher(HttpStatus.Error, []) + 'statusCodes' | [200, 201, 204] | 200 | new StatusCodeMatcher(HttpStatus.StatusCodes, [200, 201, 204]) + } + def 'allows setting the body of the response as a string value'() { when: def response = builder @@ -195,7 +243,8 @@ class HttpResponseBuilderSpec extends Specification { .build() then: - response.body.valueAsString() == '\n\n' + response.body.valueAsString() == '' + + System.lineSeparator() + '' + System.lineSeparator() response.body.contentType.toString() == 'application/xml' response.headers['content-type'] == ['application/xml'] response.matchingRules.rulesForCategory('body') == new MatchingRuleCategory('body', @@ -226,105 +275,4 @@ class HttpResponseBuilderSpec extends Specification { ] ) } - - // /** - // * Match a set cookie header - // * @param cookie Cookie name to match - // * @param regex Regex to match the cookie value with - // * @param example Example value - // */ - // fun matchSetCookie(cookie: String, regex: String, example: String): PactDslResponse { - // val header = responseMatchers.addCategory("header") - // if (header.numRules("set-cookie") > 0) { - // header.addRule("set-cookie", RegexMatcher(Pattern.quote("$cookie=") + regex)) - // } else { - // header.setRule("set-cookie", RegexMatcher(Pattern.quote("$cookie=") + regex), RuleLogic.OR) - // } - // if (responseHeaders.containsKey("set-cookie")) { - // responseHeaders["set-cookie"] = responseHeaders["set-cookie"]!!.plus("$cookie=$example") - // } else { - // responseHeaders["set-cookie"] = listOf("$cookie=$example") - // } - // return this - // } - - // /** - // * Match any HTTP Information response status (100-199) - // */ - // fun informationStatus(): PactDslResponse { - // val matcher = StatusCodeMatcher(HttpStatus.Information) - // responseMatchers.addCategory("status").addRule(matcher) - // responseStatus = 100 - // return this - // } - // - // /** - // * Match any HTTP success response status (200-299) - // */ - // fun successStatus(): PactDslResponse { - // val matcher = StatusCodeMatcher(HttpStatus.Success) - // responseMatchers.addCategory("status").addRule(matcher) - // responseStatus = 200 - // return this - // } - // - // /** - // * Match any HTTP redirect response status (300-399) - // */ - // fun redirectStatus(): PactDslResponse { - // val matcher = StatusCodeMatcher(HttpStatus.Redirect) - // responseMatchers.addCategory("status").addRule(matcher) - // responseStatus = 300 - // return this - // } - // - // /** - // * Match any HTTP client error response status (400-499) - // */ - // fun clientErrorStatus(): PactDslResponse { - // val matcher = StatusCodeMatcher(HttpStatus.ClientError) - // responseMatchers.addCategory("status").addRule(matcher) - // responseStatus = 400 - // return this - // } - // - // /** - // * Match any HTTP server error response status (500-599) - // */ - // fun serverErrorStatus(): PactDslResponse { - // val matcher = StatusCodeMatcher(HttpStatus.ServerError) - // responseMatchers.addCategory("status").addRule(matcher) - // responseStatus = 500 - // return this - // } - // - // /** - // * Match any HTTP non-error response status (< 400) - // */ - // fun nonErrorStatus(): PactDslResponse { - // val matcher = StatusCodeMatcher(HttpStatus.NonError) - // responseMatchers.addCategory("status").addRule(matcher) - // responseStatus = 200 - // return this - // } - // - // /** - // * Match any HTTP error response status (>= 400) - // */ - // fun errorStatus(): PactDslResponse { - // val matcher = StatusCodeMatcher(HttpStatus.Error) - // responseMatchers.addCategory("status").addRule(matcher) - // responseStatus = 400 - // return this - // } - // - // /** - // * Match any HTTP status code in the provided list - // */ - // fun statusCodes(statusCodes: List): PactDslResponse { - // val matcher = StatusCodeMatcher(HttpStatus.StatusCodes, statusCodes) - // responseMatchers.addCategory("status").addRule(matcher) - // responseStatus = statusCodes.first() - // return this - // } }