Skip to content

Commit

Permalink
fix: regression with publishing verification results
Browse files Browse the repository at this point in the history
  • Loading branch information
Ronald Holshausen committed Aug 11, 2020
1 parent ff62a8d commit d0330b0
Show file tree
Hide file tree
Showing 16 changed files with 202 additions and 127 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ public void provider2Fails() throws IOException {
mockTestProvider2.validateResultWith((result, t) -> {
assertThat(t, is(instanceOf(AssertionError.class)));
assertThat(t.getMessage(), is("The following mismatched requests occurred:\n" +
"BodyMismatch: $.name Expected 'larry' (String) but received 'farry' (String)"));
"$.name Expected 'larry' (String) but received 'farry' (String)"));
assertThat(result, is(instanceOf(PactVerificationResult.Mismatches.class)));
PactVerificationResult.Mismatches error = (PactVerificationResult.Mismatches) result;
assertThat(error.getMismatches(), hasSize(1));
Expand Down Expand Up @@ -152,7 +152,7 @@ public void bothprovidersFail() throws IOException {
mockTestProvider2.validateResultWith((result, t) -> {
assertThat(t, is(instanceOf(AssertionError.class)));
assertThat(t.getMessage(), is("The following mismatched requests occurred:\n" +
"BodyMismatch: $.name Expected 'larry' (String) but received 'farry' (String)"));
"$.name Expected 'larry' (String) but received 'farry' (String)"));
assertThat(result, is(instanceOf(PactVerificationResult.Mismatches.class)));
PactVerificationResult.Mismatches error = (PactVerificationResult.Mismatches) result;
assertThat(error.getMismatches(), hasSize(1));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,28 @@ interface MismatchFactory<out M : Mismatch> {
sealed class Mismatch {
open fun description() = this::class.java.simpleName + ": " + this.toString()
open fun description(t: TermColors) = this.description()
fun type() = this::class.java.simpleName
open fun type(): String = this::class.java.simpleName
}

data class StatusMismatch(val expected: Int, val actual: Int) : Mismatch() {
override fun description() = "StatusMismatch: expected status of $expected but was $actual"
override fun description() = "expected status of $expected but was $actual"
override fun description(t: TermColors) =
"expected status of ${t.bold(expected.toString())} but was ${t.bold(actual.toString())}"
fun toMap(): Map<String, Any?> {
return mapOf("mismatch" to description())
}
override fun type() = "status"
}

data class BodyTypeMismatch(val expected: String?, val actual: String?) : Mismatch() {
override fun description() = "BodyTypeMismatch: Expected a response type of '$expected' " +
override fun description() = "Expected a response type of '$expected' " +
"but the actual type was '$actual'"
override fun description(t: TermColors) =
"Expected a response type of ${t.bold("'$expected'")} but the actual type was ${t.bold("'$actual'")}"
fun toMap(): Map<String, Any?> {
return mapOf("mismatch" to description())
}
override fun type() = "body-content-type"
}

data class CookieMismatch(val expected: List<String>, val actual: List<String>) : Mismatch()
Expand All @@ -45,24 +47,29 @@ data class PathMismatch @JvmOverloads constructor (
) : Mismatch() {
override fun description() = when (mismatch) {
null -> super.description()
else -> "PathMismatch: $mismatch"
else -> mismatch
}
override fun type() = "path"
}

object PathMismatchFactory : MismatchFactory<PathMismatch> {
override fun create(expected: Any?, actual: Any?, message: String, path: List<String>) =
PathMismatch(expected.toString(), actual.toString(), message)
}

data class MethodMismatch(val expected: String, val actual: String) : Mismatch()
data class MethodMismatch(val expected: String, val actual: String) : Mismatch() {
override fun type() = "method"
}

data class QueryMismatch(
val queryParameter: String,
val expected: String,
val actual: String,
val mismatch: String? = null,
val path: String = "/"
) : Mismatch()
) : Mismatch() {
override fun type() = "query"
}

object QueryMismatchFactory : MismatchFactory<QueryMismatch> {
override fun create(expected: Any?, actual: Any?, message: String, path: List<String>) =
Expand All @@ -76,10 +83,11 @@ data class HeaderMismatch(
val mismatch: String
) : Mismatch() {
val regex = Regex("'[^']*'")
override fun description() = "HeaderMismatch: $mismatch"
override fun description() = mismatch
override fun description(t: TermColors): String {
return mismatch.replace(regex) { m -> t.bold(m.value) }
}
override fun type() = "header"

fun merge(mismatch: HeaderMismatch): HeaderMismatch {
return if (this.mismatch.isNotEmpty()) {
Expand All @@ -106,7 +114,8 @@ data class BodyMismatch @JvmOverloads constructor(
val path: String = "/",
val diff: String? = null
) : Mismatch() {
override fun description() = "BodyMismatch: $path $mismatch"
override fun description() = "$path $mismatch"
override fun type() = "body"
}

object BodyMismatchFactory : MismatchFactory<BodyMismatch> {
Expand All @@ -115,7 +124,8 @@ object BodyMismatchFactory : MismatchFactory<BodyMismatch> {
}

data class MetadataMismatch(val key: String, val expected: Any?, val actual: Any?, val mismatch: String) : Mismatch() {
override fun description() = "MetadataMismatch: $mismatch"
override fun description() = mismatch
override fun type() = "metadata"

fun merge(mismatch: MetadataMismatch) = copy(mismatch = this.mismatch + ", " + mismatch.mismatch)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -532,9 +532,9 @@ class XmlBodyMatcherSpec extends Specification {

then:
result.size() == 3
result*.description() == ['BodyMismatch: $.animals.dog.1 Unexpected child <dog/>',
'BodyMismatch: $.animals.cat.1 Unexpected child <cat/>',
'BodyMismatch: $.animals.cat.2 Unexpected child <cat/>']
result*.description() == ['$.animals.dog.1 Unexpected child <dog/>',
'$.animals.cat.1 Unexpected child <cat/>',
'$.animals.cat.2 Unexpected child <cat/>']
}

def 'type matcher when an element has different types of children'() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,45 +285,12 @@ open class PactBrokerClient(val pactBrokerUrl: String, override val options: Map
.map { mismatches ->
val values = mismatches.value
.filter { !it.containsKey("exception") }
.flatMap { mismatch ->
when (mismatch["type"]) {
"body" -> {
when (val bodyMismatches = mismatch["comparison"]) {
is Map<*, *> -> bodyMismatches.entries.filter { it.key != "diff" }.flatMap { entry ->
val values = entry.value as List<Map<String, Any>>
values.map {
jsonObject("attribute" to "body", "identifier" to entry.key, "description" to it["mismatch"],
"diff" to it["diff"])
}
}
else -> listOf(jsonObject("attribute" to "body", "description" to bodyMismatches.toString()))
}
}
"status" -> listOf(jsonObject("attribute" to "status", "description" to mismatch["description"]))
"header" -> {
listOf(jsonObject(mismatch.filter { it.key != "interactionId" }
.map {
if (it.key == "type") {
"attribute" to it.value
} else {
it.toPair()
}
}))
}
"metadata" -> {
listOf(jsonObject(mismatch.filter { it.key != "interactionId" }
.flatMap {
when (it.key) {
"type" -> listOf("attribute" to it.value)
else -> listOf("identifier" to it.key, "description" to it.value)
}
}))
}
else -> listOf(jsonObject(
mismatch.filterNot { it.key == "interactionId" || it.key == "type" }.entries.map {
it.toPair()
}
))
.map { mismatch ->
when (mismatch["attribute"]) {
"body-content-type" -> jsonObject("attribute" to "body", "description" to mismatch["description"])
else -> jsonObject(
mismatch.filterNot { it.key == "interactionId" }.map { it.toPair() }
)
}
}
val interactionJson = jsonObject("interactionId" to mismatches.key, "success" to false,
Expand All @@ -333,9 +300,15 @@ open class PactBrokerClient(val pactBrokerUrl: String, override val options: Map
val exceptionDetails = mismatches.value.find { it.containsKey("exception") }
if (exceptionDetails != null) {
val exception = exceptionDetails["exception"]
val description = exceptionDetails["description"]
if (exception is Throwable) {
interactionJson["exceptions"] = jsonArray(jsonObject("message" to exception.message,
"exceptionClass" to exception.javaClass.name))
if (description != null) {
interactionJson["exceptions"] = jsonArray(jsonObject("message" to description.toString() + ": " + exception.message,
"exceptionClass" to exception.javaClass.name))
} else {
interactionJson["exceptions"] = jsonArray(jsonObject("message" to exception.message,
"exceptionClass" to exception.javaClass.name))
}
} else {
interactionJson["exceptions"] = jsonArray(jsonObject("message" to exception.toString()))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@ class VerificationResultPayloadSpec extends Specification {
}

Map buildPayload() {
new JsonSlurper().parseText(pactBrokerClient.buildPayload(result, version, buildUrl).serialise()) as Map
def json = pactBrokerClient.buildPayload(result, version, buildUrl).serialise()
new JsonSlurper().parseText(json) as Map
}

def 'exceptions should be serialised as a message and exception class'() {
given:
result = new TestResult.Failed([
[message: 'test failed', 'exception': new IOException('Boom'), interactionId: 'ABC'],
[description: 'Expected status code of 400 but got 500', interactionId: 'ABC', type: 'status']
[description: 'Expected status code of 400 but got 500', interactionId: 'ABC', attribute: 'status']
], 'Test failed with exception')

when:
Expand All @@ -47,9 +48,9 @@ class VerificationResultPayloadSpec extends Specification {
def 'mismatches should be grouped by interaction'() {
given:
result = new TestResult.Failed([
[description: 'Expected status code of 400 but got 500', interactionId: 'ABC', type: 'status'],
[description: 'Expected status code of 400 but got 500', interactionId: '123', type: 'status'],
[description: 'Expected status code of 200 but got 500', interactionId: 'ABC', type: 'status'],
[description: 'Expected status code of 400 but got 500', interactionId: 'ABC', attribute: 'status'],
[description: 'Expected status code of 400 but got 500', interactionId: '123', attribute: 'status'],
[description: 'Expected status code of 200 but got 500', interactionId: 'ABC', attribute: 'status'],
[message: 'test failed', 'exception': new IOException('Boom'), interactionId: '123']
], 'Test failed')

Expand Down Expand Up @@ -94,42 +95,23 @@ class VerificationResultPayloadSpec extends Specification {

result = new TestResult.Failed([
[
comparison: [
'$.0': [
[
mismatch: "Expected doesNotExist='Test' but was missing",
diff: diff
]
],
'$.1': [
[
mismatch: "Expected doesNotExist='Test' but was missing",
diff: diff
]
],
diff: [
' {',
'- "doesNotExist": "Test"',
'',
'- "documentId": 0',
'+ "documentId": 0',
'',
'+ "documentCategoryId": 5',
'',
'+ "documentCategoryCode": null',
'',
'+ "contentLength": 0',
'',
'+ "tags": null',
'+ }']
],
interactionId: '36803e0333e8967092c2910b9d2f75c033e696ee',
type: 'body'
identifier: '$.0',
description: "Expected doesNotExist='Test' but was missing",
diff: diff,
attribute: 'body',
interactionId: '36803e0333e8967092c2910b9d2f75c033e696ee'
],
[
identifier: '$.1',
description: "Expected doesNotExist='Test' but was missing",
diff: diff,
attribute: 'body',
interactionId: '36803e0333e8967092c2910b9d2f75c033e696ee'
],
[
comparison: "Expected a response type of 'application/json' but the actual type was 'text/plain'",
description: "Expected a response type of 'application/json' but the actual type was 'text/plain'",
interactionId: '1234',
type: 'body'
attribute: 'body'
]
], 'Test failed')

Expand All @@ -141,12 +123,10 @@ class VerificationResultPayloadSpec extends Specification {
then:
result.testResults.size() == 2
result1.mismatches.size() == 2
result1.mismatches[0] == [
attribute: 'body',
identifier: '$.0',
description: "Expected doesNotExist='Test' but was missing",
diff: diff
]
result1.mismatches[0].attribute == 'body'
result1.mismatches[0].identifier == '$.0'
result1.mismatches[0].description == "Expected doesNotExist='Test' but was missing"
result1.mismatches[0].diff == diff
result1.mismatches[1] == [
attribute: 'body',
identifier: '$.1',
Expand All @@ -167,13 +147,13 @@ class VerificationResultPayloadSpec extends Specification {
identifier: 'X',
description: "Expected header 'X' to have value '100' but was '200'",
interactionId: '36803e0333e8967092c2910b9d2f75c033e696ee',
type: 'header'
attribute: 'header'
],
[
identifier: 'Y',
description: "Expected header 'Y' to have value 'X' but was '100'",
interactionId: '36803e0333e8967092c2910b9d2f75c033e696ee',
type: 'header'
attribute: 'header'
]
], 'Test failed')

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,8 @@ open class InteractionRunner<I>(
testResult = VerificationResult.Failed(listOf(mapOf("message" to "Request to provider failed with an exception",
"exception" to e)),
"Request to provider failed with an exception", description.displayName,
listOf(VerificationFailureType.ExceptionFailure(e)), pending, interaction.interactionId)
listOf(VerificationFailureType.ExceptionFailure("Request to provider failed with an exception", e)),
pending, interaction.interactionId)
} finally {
if (pact is FilteredPact) {
testResultAccumulator.updateTestResult(pact.pact, interaction, testResult.toTestResult(), pactSource)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@ data class PactVerificationContext @JvmOverloads constructor(
listOf(VerificationResult.Failed(listOf(mapOf("message" to "Request to provider failed with an exception",
"exception" to e)),
"Request to provider failed with an exception", interactionMessage,
listOf(VerificationFailureType.ExceptionFailure(e)), consumer.pending, interaction.interactionId))
listOf(VerificationFailureType.ExceptionFailure("Request to provider failed with an exception", e)),
consumer.pending, interaction.interactionId))
}
} else {
return listOf(verifier!!.verifyResponseByInvokingProviderMethods(providerInfo, consumer, interaction,
Expand Down Expand Up @@ -322,9 +323,9 @@ class PactVerificationStateChangeExtension(
} catch (e: Exception) {
val pending = pactSource is BrokerUrlSource && pactSource.result?.pending == true
logger.error(e) { "Provider state change callback failed" }
testContext.testExecutionResult.add(VerificationResult.Failed(description = "Provider state change teardown callback failed",
testContext.testExecutionResult.add(VerificationResult.Failed(description = "Provider state change callback failed",
results = listOf(mapOf("exception" to e)),
failures = listOf(VerificationFailureType.StateChangeFailure(StateChangeResult(Err(e)))),
failures = listOf(VerificationFailureType.StateChangeFailure("Provider state change callback failed", StateChangeResult(Err(e)))),
pending = pending,
interactionId = interaction.interactionId
))
Expand All @@ -346,7 +347,7 @@ class PactVerificationStateChangeExtension(
logger.error(e) { "Provider state change callback failed" }
testContext.testExecutionResult.add(VerificationResult.Failed(description = "Provider state change teardown callback failed",
results = listOf(mapOf("exception" to e)),
failures = listOf(VerificationFailureType.StateChangeFailure(StateChangeResult(Err(e)))),
failures = listOf(VerificationFailureType.StateChangeFailure("Provider state change teardown callback failed", StateChangeResult(Err(e)))),
pending = pending,
interactionId = interaction.interactionId
))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ open class MvcProviderVerifier(private val debugRequestResponse: Boolean = false
return VerificationResult.Failed(listOf(mapOf("message" to "Request to provider method failed with an exception",
"exception" to e)),
"Request to provider method failed with an exception", interactionMessage,
listOf(VerificationFailureType.ExceptionFailure(e)), pending, interaction.interactionId)
listOf(VerificationFailureType.ExceptionFailure("Request to provider method failed with an exception", e)),
pending, interaction.interactionId)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ class WebFluxProviderVerifier : ProviderVerifier() {
return VerificationResult.Failed(
listOf(mapOf("message" to "Request to provider method failed with an exception", "exception" to e)),
"Request to provider method failed with an exception", interactionMessage,
listOf(VerificationFailureType.ExceptionFailure(e)), pending, interaction.interactionId)
listOf(VerificationFailureType.ExceptionFailure("Request to provider method failed with an exception", e)),
pending, interaction.interactionId)
}
}

Expand Down
Loading

0 comments on commit d0330b0

Please sign in to comment.