All Downloads are FREE. Search and download functionalities are using the official Maven repository.

au.com.dius.pact.provider.TestResultAccumulator.kt Maven / Gradle / Ivy

package au.com.dius.pact.provider

import au.com.dius.pact.core.model.BrokerUrlSource
import au.com.dius.pact.core.model.Interaction
import au.com.dius.pact.core.model.Pact
import au.com.dius.pact.core.model.PactSource
import au.com.dius.pact.core.pactbroker.TestResult
import au.com.dius.pact.core.support.Result
import au.com.dius.pact.core.support.expressions.SystemPropertyResolver
import au.com.dius.pact.core.support.expressions.ValueResolver
import au.com.dius.pact.core.support.isNotEmpty
import au.com.dius.pact.provider.ProviderVerifier.Companion.PACT_VERIFIER_PUBLISH_RESULTS
import mu.KLogging
import org.apache.commons.lang3.builder.HashCodeBuilder

/**
 * Accumulates the test results for the interactions. Once all the interactions for a pact have been verified,
 * the result is submitted back to the broker
 */
interface TestResultAccumulator {
  fun updateTestResult(
    pact: Pact,
    interaction: Interaction,
    testExecutionResult: List,
    source: PactSource,
    propertyResolver: ValueResolver = SystemPropertyResolver
  ): Result>
  fun updateTestResult(
    pact: Pact,
    interaction: Interaction,
    testExecutionResult: TestResult,
    source: PactSource?,
    propertyResolver: ValueResolver = SystemPropertyResolver
  ): Result>
  fun clearTestResult(pact: Pact, source: PactSource?)
}

object DefaultTestResultAccumulator : TestResultAccumulator, KLogging() {

  val testResults: MutableMap> = mutableMapOf()
  var verificationReporter: VerificationReporter = DefaultVerificationReporter

  override fun updateTestResult(
    pact: Pact,
    interaction: Interaction,
    testExecutionResult: List,
    source: PactSource,
    propertyResolver: ValueResolver
  ): Result> {
    val initial = TestResult.Ok(interaction.interactionId)
    return updateTestResult(pact, interaction, testExecutionResult.fold(initial) {
      acc: TestResult, r -> acc.merge(r.toTestResult())
    }, source, propertyResolver)
  }

  override fun updateTestResult(
    pact: Pact,
    interaction: Interaction,
    testExecutionResult: TestResult,
    source: PactSource?,
    propertyResolver: ValueResolver
  ): Result> {
    logger.debug { "Received test result '$testExecutionResult' for Pact ${pact.provider.name}-${pact.consumer.name} " +
      "and ${interaction.description} (${source?.description()})" }
    val pactHash = calculatePactHash(pact, source)
    val interactionResults = testResults.getOrPut(pactHash) { mutableMapOf() }
    val interactionHash = calculateInteractionHash(interaction)
    val testResult = interactionResults[interactionHash]
    if (testResult == null) {
      interactionResults[interactionHash] = testExecutionResult
    } else {
      interactionResults[interactionHash] = testResult.merge(testExecutionResult)
    }
    val unverifiedInteractions = unverifiedInteractions(pact, interactionResults)
    return if (unverifiedInteractions.isEmpty()) {
      logger.debug {
        "All interactions for Pact ${pact.provider.name}-${pact.consumer.name} have a verification result"
      }
      val result = if (verificationReporter.publishingResultsDisabled(propertyResolver)) {
        logger.warn {
          "Skipping publishing of verification results as it has been disabled " +
            "($PACT_VERIFIER_PUBLISH_RESULTS is not 'true')"
        }
        Result.Ok(false)
      } else {
        val calculatedTestResult = interactionResults.values.reduce { acc: TestResult, result -> acc.merge(result) }
        verificationReporter.reportResults(pact, calculatedTestResult, lookupProviderVersion(propertyResolver),
          null, lookupProviderTags(propertyResolver), lookupProviderBranch(propertyResolver))
      }
      testResults.remove(pactHash)
      result
    } else {
      logger.warn { "Not all of the ${pact.interactions.size} were verified. The following were missing:" }
      unverifiedInteractions.forEach {
        logger.warn { "    ${it.description}" }
      }
      Result.Ok(true)
    }
  }

  fun calculateInteractionHash(interaction: Interaction): Int {
    val builder = HashCodeBuilder().append(interaction.description)
    interaction.providerStates.forEach { builder.append(it.name) }
    return builder.toHashCode()
  }

  fun calculatePactHash(pact: Pact, source: PactSource?): Int {
    val builder = HashCodeBuilder(91, 47).append(pact.consumer.name).append(pact.provider.name)

    if (source is BrokerUrlSource && source.url.isNotEmpty()) {
      builder.append(source.url)
    }

    return builder.toHashCode()
  }

  fun lookupProviderVersion(propertyResolver: ValueResolver): String {
    val version = ProviderVersion { propertyResolver.resolveValue("pact.provider.version", "") }.get()
    return version.ifEmpty {
      logger.warn { "Set the provider version using the 'pact.provider.version' property. Defaulting to '0.0.0'" }
      "0.0.0"
    }
  }

  private fun lookupProviderTags(propertyResolver: ValueResolver) = propertyResolver
    .resolveValue("pact.provider.tag", "")
    .orEmpty()
    .split(',')
    .map { it.trim() }
    .filter { it.isNotEmpty() }

  private fun lookupProviderBranch(propertyResolver: ValueResolver) = propertyResolver
    .resolveValue("pact.provider.branch", "")

  fun unverifiedInteractions(pact: Pact, results: MutableMap): List {
    logger.debug { "Number of interactions #${pact.interactions.size} and results: ${results.values}" }
    return pact.interactions.filter { !results.containsKey(calculateInteractionHash(it)) }
  }

  override fun clearTestResult(pact: Pact, source: PactSource?) {
    val pactHash = calculatePactHash(pact, source)
    testResults.remove(pactHash)
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy