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

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.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()
    }
  }

  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 - 2025 Weber Informatics LLC | Privacy Policy