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

au.com.dius.pact.provider.reporters.AnsiConsoleReporter.kt Maven / Gradle / Ivy

Go to download

Pact provider ============= sub project of https://github.com/DiUS/pact-jvm The pact provider is responsible for verifying that an API provider adheres to a number of pacts authored by its clients This library provides the basic tools required to automate the process, and should be usable on its own in many instances. Framework and build tool specific bindings will be provided in separate libraries that build on top of this core functionality. ### Provider State Before each interaction is executed, the provider under test will have the opportunity to enter a state. Generally the state maps to a set of fixture data for mocking out services that the provider is a consumer of (they will have their own pacts) The pact framework will instruct the test server to enter that state by sending: POST "${config.stateChangeUrl.url}/setup" { "state" : "${interaction.stateName}" } ### An example of running provider verification with junit This example uses Groovy, JUnit 4 and Hamcrest matchers to run the provider verification. As the provider service is a DropWizard application, it uses the DropwizardAppRule to startup the service before running any test. **Warning:** It only grabs the first interaction from the pact file with the consumer, where there could be many. (This could possibly be solved with a parameterized test) ```groovy class ReadmeExamplePactJVMProviderJUnitTest { @ClassRule public static final TestRule startServiceRule = new DropwizardAppRule<DropwizardConfiguration>( TestDropwizardApplication, ResourceHelpers.resourceFilePath('dropwizard/test-config.yaml')) private static ProviderInfo serviceProvider private static Pact<RequestResponseInteraction> testConsumerPact private static ConsumerInfo consumer @BeforeClass static void setupProvider() { serviceProvider = new ProviderInfo('Dropwizard App') serviceProvider.setProtocol('http') serviceProvider.setHost('localhost') serviceProvider.setPort(8080) serviceProvider.setPath('/') consumer = new ConsumerInfo() consumer.setName('test_consumer') consumer.setPactSource(new UrlSource( ReadmeExamplePactJVMProviderJUnitTest.getResource('/pacts/zoo_app-animal_service.json').toString())) testConsumerPact = DefaultPactReader.INSTANCE.loadPact(consumer.getPactSource()) as Pact<RequestResponseInteraction> } @Test void runConsumerPacts() { // grab the first interaction from the pact with consumer Interaction interaction = testConsumerPact.interactions.get(0) // setup the verifier ProviderVerifier verifier = setupVerifier(interaction, serviceProvider, consumer) // setup any provider state // setup the client and interaction to fire against the provider ProviderClient client = new ProviderClient(serviceProvider, new HttpClientFactory()) Map<String, Object> failures = new HashMap<>() verifier.verifyResponseFromProvider(serviceProvider, interaction, interaction.getDescription(), failures, client) // normally assert all good, but in this example it will fail assertThat(failures, is(not(empty()))) verifier.displayFailures(failures) } private ProviderVerifier setupVerifier(Interaction interaction, ProviderInfo provider, ConsumerInfo consumer) { ProviderVerifier verifier = new ProviderVerifier() verifier.initialiseReporters(provider) verifier.reportVerificationForConsumer(consumer, provider, new UrlSource('http://example.example')) if (!interaction.getProviderStates().isEmpty()) { for (ProviderState providerState: interaction.getProviderStates()) { verifier.reportStateForInteraction(providerState.getName(), provider, consumer, true) } } verifier.reportInteractionDescription(interaction) return verifier } } ``` ### An example of running provider verification with spock This example uses groovy and spock to run the provider verification. Again the provider service is a DropWizard application, and is using the DropwizardAppRule to startup the service. This example runs all interactions using spocks Unroll feature ```groovy class ReadmeExamplePactJVMProviderSpockSpec extends Specification { @ClassRule @Shared TestRule startServiceRule = new DropwizardAppRule<DropwizardConfiguration>(TestDropwizardApplication, ResourceHelpers.resourceFilePath('dropwizard/test-config.yaml')) @Shared ProviderInfo serviceProvider ProviderVerifier verifier def setupSpec() { serviceProvider = new ProviderInfo('Dropwizard App') serviceProvider.protocol = 'http' serviceProvider.host = 'localhost' serviceProvider.port = 8080 serviceProvider.path = '/' serviceProvider.hasPactWith('zoo_app') { consumer -> consumer.pactSource = new FileSource(new File(ResourceHelpers.resourceFilePath('pacts/zoo_app-animal_service.json'))) } } def setup() { verifier = new ProviderVerifier() } def cleanup() { // cleanup provider state // ie. db.truncateAllTables() } def cleanupSpec() { // cleanup provider } @Unroll def "Provider Pact - With Consumer #consumer"() { expect: !verifyConsumerPact(consumer).empty where: consumer << serviceProvider.consumers } private Map verifyConsumerPact(ConsumerInfo consumer) { Map failures = [:] verifier.initialiseReporters(serviceProvider) verifier.runVerificationForConsumer(failures, serviceProvider, consumer) if (!failures.empty) { verifier.displayFailures(failures) } failures } } ```

There is a newer version: 4.0.10
Show newest version
package au.com.dius.pact.provider.reporters

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.model.UrlPactSource
import au.com.dius.pact.core.pactbroker.VerificationNotice
import au.com.dius.pact.provider.IConsumerInfo
import au.com.dius.pact.provider.IProviderInfo
import org.fusesource.jansi.Ansi
import org.fusesource.jansi.AnsiConsole
import java.io.File

/**
 * Pact verifier reporter that displays the results of the verification to the console using ASCII escapes
 */
class AnsiConsoleReporter(
  var name: String,
  override var reportDir: File?,
  var displayFullDiff: Boolean
) : VerifierReporter {

  constructor(name: String, reportDir: File?) : this(name, reportDir, false)

  override val ext: String? = null

  override var reportFile: File
    get() = TODO("not implemented")
    set(value) {}

  override fun includesMetadata() {
    AnsiConsole.out().println("      includes message metadata")
  }

  override fun metadataComparisonOk() {
    AnsiConsole.out().println(Ansi.ansi().a("      has matching metadata (")
      .fg(Ansi.Color.GREEN).a("OK").reset().a(")"))
  }

  override fun metadataComparisonOk(key: String, value: Any?) {
    AnsiConsole.out().println(Ansi.ansi().a("        \"").bold().a(key).boldOff().a("\" with value \"")
      .bold().a(value).boldOff().a("\" (").fg(Ansi.Color.GREEN).a("OK").reset().a(")"))
  }

  override fun metadataComparisonFailed(key: String, value: Any?, comparison: Any) {
    AnsiConsole.out().println(Ansi.ansi().a("        \"").bold().a(key).boldOff().a("\" with value \"")
      .bold().a(value).boldOff().a("\" (").fg(Ansi.Color.RED).a("FAILED").reset().a(")"))
  }

  override fun initialise(provider: IProviderInfo) { }

  override fun finaliseReport() { }

  override fun reportVerificationForConsumer(consumer: IConsumerInfo, provider: IProviderInfo, tag: String?) {
    val ansi = Ansi.ansi().a("\nVerifying a pact between ").bold().a(consumer.name)
            .boldOff().a(" and ").bold().a(provider.name).boldOff()

    if (tag != null) ansi.a(" for tag ").a(tag)

    AnsiConsole.out().println(ansi)
  }

  override fun verifyConsumerFromUrl(pactUrl: UrlPactSource, consumer: IConsumerInfo) {
    AnsiConsole.out().println(Ansi.ansi().a("  [from ${pactUrl.description()}]"))
  }

  override fun verifyConsumerFromFile(pactFile: PactSource, consumer: IConsumerInfo) {
    AnsiConsole.out().println(Ansi.ansi().a("  [Using ${pactFile.description()}]"))
  }

  override fun pactLoadFailureForConsumer(consumer: IConsumerInfo, message: String) { }

  override fun warnProviderHasNoConsumers(provider: IProviderInfo) {
    AnsiConsole.out().println(Ansi.ansi().a("         ").fg(Ansi.Color.YELLOW)
      .a("WARNING: There are no consumers to verify for provider '${provider.name}'").reset())
  }

  override fun warnPactFileHasNoInteractions(pact: Pact) {
    AnsiConsole.out().println(Ansi.ansi().a("         ").fg(Ansi.Color.YELLOW)
      .a("WARNING: Pact file has no interactions")
      .reset())
  }

  override fun interactionDescription(interaction: Interaction) {
    AnsiConsole.out().println(Ansi.ansi().a("  ").a(interaction.description))
  }

  override fun stateForInteraction(state: String, provider: IProviderInfo, consumer: IConsumerInfo, isSetup: Boolean) {
    AnsiConsole.out().println(Ansi.ansi().a("  Given ").bold().a(state).boldOff())
  }

  override fun warnStateChangeIgnored(state: String, IProviderInfo: IProviderInfo, IConsumerInfo: IConsumerInfo) {
    AnsiConsole.out().println(Ansi.ansi().a("         ").fg(Ansi.Color.YELLOW)
      .a("WARNING: State Change ignored as there is no stateChange URL")
      .reset())
  }

  override fun stateChangeRequestFailedWithException(
    state: String,
    provider: IProviderInfo,
    consumer: IConsumerInfo,
    isSetup: Boolean,
    e: Exception,
    printStackTrace: Boolean
  ) {
    AnsiConsole.out().println(Ansi.ansi().a("         ")
      .fg(Ansi.Color.RED).a("State Change Request Failed - ")
      .a(e.message).reset())
    if (printStackTrace) {
      e.printStackTrace()
    }
  }

  override fun stateChangeRequestFailed(state: String, provider: IProviderInfo, isSetup: Boolean, httpStatus: String) {
    AnsiConsole.out().println(Ansi.ansi().a("         ").fg(Ansi.Color.RED)
      .a("State Change Request Failed - ")
      .a(httpStatus).reset())
  }

  override fun warnStateChangeIgnoredDueToInvalidUrl(
    state: String,
    provider: IProviderInfo,
    isSetup: Boolean,
    stateChangeHandler: Any
  ) {
    AnsiConsole.out().println(Ansi.ansi().a("         ").fg(Ansi.Color.YELLOW)
      .a("WARNING: State Change ignored as there is no stateChange URL, received \"$stateChangeHandler\"")
      .reset())
  }

  override fun requestFailed(
    provider: IProviderInfo,
    interaction: Interaction,
    interactionMessage: String,
    e: Exception,
    printStackTrace: Boolean
  ) {
    AnsiConsole.out().println(Ansi.ansi().a("      ").fg(Ansi.Color.RED).a("Request Failed - ")
      .a(e.message).reset())
    if (printStackTrace) {
      e.printStackTrace()
    }
  }

  override fun returnsAResponseWhich() {
    AnsiConsole.out().println("    returns a response which")
  }

  override fun statusComparisonOk(status: Int) {
    AnsiConsole.out().println(Ansi.ansi().a("      ").a("has status code ").bold().a(status).boldOff()
      .a(" (").fg(Ansi.Color.GREEN).a("OK").reset().a(")"))
  }

  override fun statusComparisonFailed(status: Int, comparison: Any) {
    AnsiConsole.out().println(Ansi.ansi().a("      ").a("has status code ").bold().a(status).boldOff()
      .a(" (")
      .fg(Ansi.Color.RED).a("FAILED").reset().a(")"))
  }

  override fun includesHeaders() {
    AnsiConsole.out().println("      includes headers")
  }

  override fun headerComparisonOk(key: String, value: List) {
    AnsiConsole.out().println(Ansi.ansi().a("        \"").bold().a(key).boldOff().a("\" with value \"")
      .bold()
      .a(value.joinToString(", ")).boldOff().a("\" (").fg(Ansi.Color.GREEN).a("OK").reset().a(")"))
  }

  override fun headerComparisonFailed(key: String, value: List, comparison: Any) {
    AnsiConsole.out().println(Ansi.ansi().a("        \"").bold().a(key).boldOff().a("\" with value \"")
      .bold()
      .a(value.joinToString(", ")).boldOff().a("\" (").fg(Ansi.Color.RED).a("FAILED").reset().a(")"))
  }

  override fun bodyComparisonOk() {
    AnsiConsole.out().println(Ansi.ansi().a("      ").a("has a matching body").a(" (")
      .fg(Ansi.Color.GREEN).a("OK").reset().a(")"))
  }

  override fun bodyComparisonFailed(comparison: Any) {
    AnsiConsole.out().println(Ansi.ansi().a("      ").a("has a matching body").a(" (")
      .fg(Ansi.Color.RED).a("FAILED").reset().a(")"))
  }

  override fun errorHasNoAnnotatedMethodsFoundForInteraction(interaction: Interaction) { }

  override fun verificationFailed(interaction: Interaction, e: Exception, printStackTrace: Boolean) {
    AnsiConsole.out().println(Ansi.ansi().a("      ").fg(Ansi.Color.RED).a("Verification Failed - ")
      .a(e.message).reset())
    if (printStackTrace) {
      e.printStackTrace()
    }
  }

  override fun generatesAMessageWhich() {
    AnsiConsole.out().println("    generates a message which")
  }

  override fun displayFailures(failures: Map) {
    AnsiConsole.out().println("\nFailures:\n")
    failures.entries.forEachIndexed { i, err ->
      AnsiConsole.out().println("$i) ${err.key}")
      when {
        err.value is Throwable -> displayError(err.value as Throwable)
        err.value is Map<*, *> && (err.value as Map<*, *>).containsKey("comparison") &&
          (err.value as Map<*, *>)["comparison"] is Map<*, *> -> displayDiff(err.value as Map)
        err.value is String -> AnsiConsole.out().println("      ${err.value}")
        err.value is Map<*, *> -> (err.value as Map<*, *>).forEach { (key, message) ->
          AnsiConsole.out().println("      $key -> $message")
        }
        else -> AnsiConsole.out().println("      $err")
      }
      AnsiConsole.out().println()
    }
  }

  override fun reportVerificationNoticesForConsumer(
    consumer: IConsumerInfo,
    provider: IProviderInfo,
    notices: List
  ) {
    AnsiConsole.out().println("  Notices:")
    notices.forEachIndexed { i, notice -> AnsiConsole.out().println("    ${i + 1}) ${notice.text}") }
    AnsiConsole.out().println()
  }

  private fun displayDiff(diff: Map) {
    (diff["comparison"] as Map>>).forEach { (key, messageAndDiff) ->
      messageAndDiff.forEach { mismatch ->
        AnsiConsole.out().println("      $key -> ${mismatch["mismatch"]}")
        AnsiConsole.out().println()

        val mismatchDiff = if (mismatch["diff"] is List<*>) mismatch["diff"] as List
          else listOf(mismatch["diff"].toString())
        if (mismatchDiff.any { it.isNotEmpty() }) {
          AnsiConsole.out().println("        Diff:")
          AnsiConsole.out().println()

          mismatchDiff.filter { it.isNotEmpty() }.forEach {
            it.split('\n').forEach { delta ->
              when {
                delta.startsWith('@') -> AnsiConsole.out().println(Ansi.ansi().a("        ")
                  .fg(Ansi.Color.CYAN).a(delta).reset())
                delta.startsWith('-') -> AnsiConsole.out().println(Ansi.ansi().a("        ")
                  .fg(Ansi.Color.RED).a(delta).reset())
                delta.startsWith('+') -> AnsiConsole.out().println(Ansi.ansi().a("        ")
                  .fg(Ansi.Color.GREEN).a(delta).reset())
                else -> AnsiConsole.out().println("        $delta")
              }
            }
            AnsiConsole.out().println()
          }
        }
      }
    }

    if (displayFullDiff) {
      AnsiConsole.out().println("      Full Diff:")
      AnsiConsole.out().println()

      (diff["diff"] as List).forEach { delta ->
        when {
          delta.startsWith('@') -> AnsiConsole.out().println(Ansi.ansi().a("      ")
            .fg(Ansi.Color.CYAN).a(delta).reset())
          delta.startsWith('-') -> AnsiConsole.out().println(Ansi.ansi().a("      ")
            .fg(Ansi.Color.RED).a(delta).reset())
          delta.startsWith('+') -> AnsiConsole.out().println(Ansi.ansi().a("      ")
            .fg(Ansi.Color.GREEN).a(delta).reset())
          else -> AnsiConsole.out().println("      $delta")
        }
      }
      AnsiConsole.out().println()
    }
  }

  private fun displayError(err: Throwable) {
    if (!err.message.isNullOrEmpty()) {
      err.message!!.split('\n').forEach {
        AnsiConsole.out().println("      $it")
      }
    } else {
      AnsiConsole.out().println("      ${err.javaClass.name}")
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy