Skip to content

Commit

Permalink
feat: support handling output from verification via plugins
Browse files Browse the repository at this point in the history
  • Loading branch information
Ronald Holshausen committed Apr 27, 2022
1 parent 6691a4c commit 9a88653
Show file tree
Hide file tree
Showing 14 changed files with 89 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ sealed class JsonValue {
fun append(value: JsonValue) {
values.add(value)
}

fun appendAll(list: List<JsonValue>) {
values.addAll(list)
}
}

class Object @JvmOverloads constructor (val entries: MutableMap<String, JsonValue> = mutableMapOf()) : JsonValue() {
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ junit5Version=5.8.2
bytebuddyVersion=1.12.7
kotlinLogging=2.0.10
kotlinResult=1.1.12
pluginDriverVersion=0.1.2
pluginDriverVersion=0.1.3
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ open class InteractionRunner(
for (interaction in pact.interactions) {
val description = describeChild(interaction)
val interactionId = interaction.interactionId
var testResult: VerificationResult = VerificationResult.Ok(interactionId)
var testResult: VerificationResult = VerificationResult.Ok(interactionId, emptyList())
val pending = when {
interaction.isV4() && interaction.asV4Interaction().pending -> true
pact.source is BrokerUrlSource -> (pact.source as BrokerUrlSource).result?.pending == true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,11 @@ data class PactVerificationContext @JvmOverloads constructor(
val testContext = store.get("interactionContext") as PactVerificationContext
try {
Metrics.sendMetrics(MetricEvent.ProviderVerificationRan(1, "junit5"))

val result = validateTestExecution(client, request, testContext.executionContext ?: mutableMapOf())
.filterIsInstance<VerificationResult.Failed>()
this.testExecutionResult.addAll(result)
verifier!!.displayOutput(result.flatMap { it.getResultOutput() })

this.testExecutionResult.addAll(result.filterIsInstance<VerificationResult.Failed>())
if (testExecutionResult.isNotEmpty()) {
verifier!!.displayFailures(testExecutionResult)
if (testExecutionResult.any { !it.pending }) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ abstract class MockTestingTarget(
callVerifierFn(provider, consumer, verifier, failures)
}

val initial = VerificationResult.Ok(interaction.interactionId)
val initial = VerificationResult.Ok(interaction.interactionId, emptyList())
val result = results.fold(initial) { acc: VerificationResult, r -> acc.merge(r) }

reportTestResult(result, verifier)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,11 @@ interface IProviderVerifier {
context: Map<String, Any>
): VerificationResult

/**
* Display any output to the user
*/
fun displayOutput(output: List<String>)

/**
* Source of the verification (Gradle/Maven/Junit)
*/
Expand Down Expand Up @@ -397,7 +402,7 @@ open class ProviderVerifier @JvmOverloads constructor (
failures, pending)
} else {
val expectedResponse = (interaction as SynchronousRequestResponse).response
var result: VerificationResult = VerificationResult.Ok(interactionId)
var result: VerificationResult = VerificationResult.Ok(interactionId, emptyList())
methodsAnnotatedWith.forEach {
val response = invokeProviderMethod(it, null) as Map<String, Any>
val body = OptionalBody.body(response["data"] as String?)
Expand Down Expand Up @@ -512,7 +517,7 @@ open class ProviderVerifier @JvmOverloads constructor (
): VerificationResult {
return if (comparison is Ok && comparison.value.mismatches.isEmpty()) {
emitEvent(Event.BodyComparisonOk)
VerificationResult.Ok(interactionId)
VerificationResult.Ok(interactionId, emptyList())
} else {
emitEvent(Event.BodyComparisonFailed(comparison))
val description = "$comparisonDescription has a matching body"
Expand Down Expand Up @@ -540,7 +545,7 @@ open class ProviderVerifier @JvmOverloads constructor (
pending: Boolean
): VerificationResult {
val interactionId = message.interactionId
var result: VerificationResult = VerificationResult.Ok(interactionId)
var result: VerificationResult = VerificationResult.Ok(interactionId, emptyList())
methods.forEach { method ->
val messageFactory: Function<String, Any> =
Function { invokeProviderMethod(method, providerMethodInstance.apply(method))!! }
Expand Down Expand Up @@ -620,7 +625,7 @@ open class ProviderVerifier @JvmOverloads constructor (
): VerificationResult {
return if (comparison.isEmpty()) {
emitEvent(Event.MetadataComparisonOk())
VerificationResult.Ok(interactionId)
VerificationResult.Ok(interactionId, emptyList())
} else {
emitEvent(Event.IncludesMetadata)
var result: VerificationResult = VerificationResult.Failed("Metadata had differences",
Expand Down Expand Up @@ -773,7 +778,7 @@ open class ProviderVerifier @JvmOverloads constructor (
): VerificationResult {
return if (mismatch == null) {
reporters.forEach { it.statusComparisonOk(status) }
VerificationResult.Ok(interactionId)
VerificationResult.Ok(interactionId, emptyList())
} else {
reporters.forEach { it.statusComparisonFailed(status, mismatch.description()) }
val description = "$comparisonDescription: has status code $status"
Expand All @@ -791,7 +796,7 @@ open class ProviderVerifier @JvmOverloads constructor (
interactionId: String,
pending: Boolean
): VerificationResult {
val ok = VerificationResult.Ok(interactionId)
val ok = VerificationResult.Ok(interactionId, emptyList())
return if (headers.isEmpty()) {
ok
} else {
Expand Down Expand Up @@ -965,7 +970,7 @@ open class ProviderVerifier @JvmOverloads constructor (
interaction
)) {
is Ok -> if (result.value.ok) {
VerificationResult.Ok(interaction.interactionId)
VerificationResult.Ok(interaction.interactionId, result.value.output)
} else {
VerificationResult.Failed("Verification via plugin failed", "Verification Failed",
mapOf(interaction.interactionId.orEmpty() to
Expand All @@ -976,14 +981,18 @@ open class ProviderVerifier @JvmOverloads constructor (
BodyMismatch(it.expected, it.actual, it.mismatch, it.path)
)
}
})
}), output = result.value.output
)
}
is Err -> VerificationResult.Failed("Verification via plugin failed",
"Verification Failed - ${result.error}")
}
}

override fun displayOutput(output: List<String>) {
emitEvent(Event.DisplayUserOutput(output))
}

@Suppress("TooGenericExceptionCaught", "TooGenericExceptionThrown")
fun loadPactFileForConsumer(consumer: IConsumerInfo): Pact {
var pactSource = consumer.resolvePactSource()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,18 @@ sealed class VerificationResult {
/**
* Result was successful
*/
data class Ok @JvmOverloads constructor(val interactionIds: Set<String> = emptySet()) : VerificationResult() {
data class Ok @JvmOverloads constructor(
val interactionIds: Set<String> = emptySet(),
val output: List<String> = emptyList()
) : VerificationResult() {

constructor(interactionId: String?) : this(if (interactionId.isNullOrEmpty())
emptySet() else setOf(interactionId))
constructor(
interactionId: String?,
output: List<String>
) : this(if (interactionId.isNullOrEmpty()) emptySet() else setOf(interactionId), output)

override fun merge(result: VerificationResult) = when (result) {
is Ok -> this.copy(interactionIds = interactionIds + result.interactionIds)
is Ok -> this.copy(interactionIds = interactionIds + result.interactionIds, output = output + result.output)
is Failed -> result.merge(this)
}

Expand All @@ -141,19 +146,21 @@ sealed class VerificationResult {
val failures: VerificationFailures = mapOf(),
val pending: Boolean = false,
@Deprecated("use failures instead")
var results: List<Map<String, Any?>> = emptyList()
var results: List<Map<String, Any?>> = emptyList(),
val output: List<String> = emptyList()
) : VerificationResult() {
override fun merge(result: VerificationResult) = when (result) {
is Ok -> this.copy(failures = failures + result.interactionIds
.associateWith {
(if (failures.containsKey(it)) failures[it] else emptyList<VerificationFailureType>())!!
})
}, output = output + result.output)
is Failed -> Failed(when {
description.isNotEmpty() && result.description.isNotEmpty() && description != result.description ->
"$description, ${result.description}"
description.isNotEmpty() -> description
else -> result.description
}, verificationDescription, mergeFailures(failures, result.failures), pending && result.pending)
}, verificationDescription, mergeFailures(failures, result.failures),
pending && result.pending, output = output + result.output)
}

private fun mergeFailures(failures: VerificationFailures, other: VerificationFailures): VerificationFailures {
Expand Down Expand Up @@ -209,4 +216,14 @@ sealed class VerificationResult {
* Convert to a test result
*/
abstract fun toTestResult(): TestResult

/**
* Return any output for the result
*/
fun getResultOutput(): List<String> {
return when (this) {
is Failed -> this.output
is Ok -> this.output
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,9 @@ class AnsiConsoleReporter(
override fun receive(event: Event) {
when (event) {
is Event.DisplayInteractionComments -> displayComments(event)
is Event.DisplayUserOutput -> for (line in event.output) {
println(line)
}
else -> super.receive(event)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,9 @@ sealed class Event {
data class MetadataComparisonFailed(val key: String, val value: Any?, val comparison: Any): Event()
data class InteractionDescription(val interaction: Interaction): Event()
data class DisplayInteractionComments(val comments: Map<String, JsonValue>) : Event()

/**
* Output to display to the user
*/
class DisplayUserOutput(val output: List<String>) : Event()
}
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,16 @@ class JsonReporter(
is Event.DisplayInteractionComments ->
jsonData["execution"].asArray()!!.last()["consumer"].asObject()!!["comments"] =
JsonValue.Object(event.comments.toMutableMap())
is Event.DisplayUserOutput -> {
val consumer = jsonData["execution"].asArray()!!.last()["consumer"].asObject()!!
val outputJson = event.output.map { JsonValue.StringValue(it) }

if (!consumer.has("output")) {
consumer["output"] = JsonValue.Array(outputJson.toMutableList())
} else {
consumer["output"].asArray()!!.appendAll(outputJson)
}
}
else -> super.receive(event)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,7 @@ class MarkdownReporter(
override fun receive(event: Event) {
when (event) {
is Event.DisplayInteractionComments -> events.add(MREvent("displayComments", displayComments(event)))
is Event.DisplayUserOutput -> events.add(MREvent("displayOutput", event.output.joinToString("\n")))
else -> super.receive(event)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@ class SLF4JReporter(
override fun receive(event: Event) {
when (event) {
is Event.DisplayInteractionComments -> displayComments(event)
is Event.DisplayUserOutput -> log.info(event.output.joinToString("\n"))
else -> super.receive(event)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ abstract class BaseVerifierReporter: VerifierReporter {
is Event.MetadataComparisonFailed -> metadataComparisonFailed(event.key, event.value, event.comparison)
is Event.InteractionDescription -> interactionDescription(event.interaction)
is Event.DisplayInteractionComments -> {}
else -> {}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,22 @@ class VerificationResultSpec extends Specification {

where:

result1 | result2 | result3
new VerificationResult.Ok() | new VerificationResult.Ok() | new VerificationResult.Ok()
new VerificationResult.Ok() | failed([error1], '') | failed([error1], '')
failed([error1], '') | new VerificationResult.Ok() | failed([error1], '')
failed([error1], '') | failed([error2], '') | failed([error1, error2], '')
failed([error1], 'A') | failed([error2], '') | failed([error1, error2], 'A')
failed([error1], '') | failed([error2], 'B') | failed([error1, error2], 'B')
failed([error1], 'A') | failed([error2], 'B') | failed([error1, error2], 'A, B')
failed([error1], 'A') | failed([error2], 'A') | failed([error1, error2], 'A')
failed([error1], 'A') | failed([error2], 'A') | failed([error1, error2], 'A')
failed([error1], '', true) | failed([error2], 'A', true) | failed([error1, error2], 'A', true)
failed([error1], '', true) | failed([error2], 'A', false) | failed([error1, error2], 'A', false)
failed(['1': [error1]], '') | new VerificationResult.Ok('1') | failed(['1': [error1]], '')
failed(['1': [error1]], '') | new VerificationResult.Ok('2') | failed(['1': [error1], '2': []], '')
new VerificationResult.Ok('1') | failed(['1': [error1]], '') | failed(['1': [error1]], '')
new VerificationResult.Ok('2') | failed(['1': [error1]], '') | failed(['1': [error1], '2': []], '')
result1 | result2 | result3
new VerificationResult.Ok() | new VerificationResult.Ok() | new VerificationResult.Ok()
new VerificationResult.Ok() | failed([error1], '') | failed([error1], '')
failed([error1], '') | new VerificationResult.Ok() | failed([error1], '')
failed([error1], '') | failed([error2], '') | failed([error1, error2], '')
failed([error1], 'A') | failed([error2], '') | failed([error1, error2], 'A')
failed([error1], '') | failed([error2], 'B') | failed([error1, error2], 'B')
failed([error1], 'A') | failed([error2], 'B') | failed([error1, error2], 'A, B')
failed([error1], 'A') | failed([error2], 'A') | failed([error1, error2], 'A')
failed([error1], 'A') | failed([error2], 'A') | failed([error1, error2], 'A')
failed([error1], '', true) | failed([error2], 'A', true) | failed([error1, error2], 'A', true)
failed([error1], '', true) | failed([error2], 'A', false) | failed([error1, error2], 'A', false)
failed(['1': [error1]], '') | new VerificationResult.Ok('1', []) | failed(['1': [error1]], '')
failed(['1': [error1]], '') | new VerificationResult.Ok('2', []) | failed(['1': [error1], '2': []], '')
new VerificationResult.Ok('1', []) | failed(['1': [error1]], '') | failed(['1': [error1]], '')
new VerificationResult.Ok('2', []) | failed(['1': [error1]], '') | failed(['1': [error1], '2': []], '')
}

def 'convert to TestResult - Exception'() {
Expand Down

0 comments on commit 9a88653

Please sign in to comment.