From 57c0bc85976b9c6b9dc96d152b6a6fecc4c84c89 Mon Sep 17 00:00:00 2001 From: Ronald Holshausen Date: Thu, 11 Jul 2024 10:34:14 +1000 Subject: [PATCH] feat(pact_consumer): Improve output of mock server errors with ANSI colour --- rust/Cargo.lock | 11 +- rust/pact_consumer/Cargo.toml | 4 +- .../src/mock_server/http_mock_server.rs | 103 +++++++++++++----- 3 files changed, 86 insertions(+), 32 deletions(-) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index aa9a1870..c9fe9643 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -123,7 +123,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72fe02fc62033df9ba41cba57ee19acf5e742511a140c7dbc3a873e19a19a1bd" dependencies = [ "unicode-width", - "yansi", + "yansi 0.5.1", ] [[package]] @@ -1945,6 +1945,7 @@ dependencies = [ "tracing-subscriber", "url", "uuid", + "yansi 1.0.1", ] [[package]] @@ -2379,7 +2380,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" dependencies = [ "diff", - "yansi", + "yansi 0.5.1", ] [[package]] @@ -4348,6 +4349,12 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + [[package]] name = "zeroize" version = "1.8.1" diff --git a/rust/pact_consumer/Cargo.toml b/rust/pact_consumer/Cargo.toml index 8fbc45db..bc1745b6 100644 --- a/rust/pact_consumer/Cargo.toml +++ b/rust/pact_consumer/Cargo.toml @@ -14,12 +14,13 @@ exclude = [ ] [features] -default = ["datetime", "xml", "plugins", "multipart", "tls"] +default = ["datetime", "xml", "plugins", "multipart", "tls", "colour"] datetime = ["pact_models/datetime", "pact_matching/datetime", "pact_mock_server/datetime", "pact-plugin-driver?/datetime"] # Support for date/time matchers and expressions xml = ["pact_models/xml", "pact_matching/xml", "pact_mock_server/xml", "pact-plugin-driver?/xml"] # support for matching XML documents plugins = ["dep:pact-plugin-driver", "pact_matching/plugins", "pact_mock_server/plugins"] multipart = ["pact_matching/multipart", "pact_mock_server/multipart"] # suport for MIME multipart bodies tls = ["pact_mock_server/tls"] +colour = ["dep:yansi"] [dependencies] anyhow = "1.0.82" @@ -41,6 +42,7 @@ tracing = "0.1.40" tracing-core = "0.1.32" url = "2.5.0" uuid = { version = "1.8.0", features = ["v4"] } +yansi = { version = "1.0.1", optional = true } [dev-dependencies] quickcheck = "1.0.3" diff --git a/rust/pact_consumer/src/mock_server/http_mock_server.rs b/rust/pact_consumer/src/mock_server/http_mock_server.rs index c9ed4bde..6ac15b6f 100644 --- a/rust/pact_consumer/src/mock_server/http_mock_server.rs +++ b/rust/pact_consumer/src/mock_server/http_mock_server.rs @@ -13,6 +13,7 @@ use pact_models::pact::Pact; use tracing::{debug, warn}; use url::Url; use uuid::Uuid; +#[cfg(feature = "colour")] use yansi::Paint; use pact_matching::metrics::{MetricEvent, send_metrics}; use pact_mock_server::matching::MatchResult; @@ -239,40 +240,84 @@ impl ValidatingHttpMockServer { Ok(()) } else { // Failure. Format our errors. - let size = termsize::get() - .map(|sz| if sz.cols > 2 { sz.cols - 2 } else { 0 }) - .unwrap_or(78); - let pad = "-".repeat(size as usize); - let mut msg = format!(" {} \nMock server {} failed verification:\n", pad, self.description); - for mismatch in mismatches { - match mismatch { - MatchResult::RequestMatch(..) => { - warn!("list of mismatches contains a match"); - } - MatchResult::RequestMismatch(request, _, mismatches) => { - let _ = writeln!(&mut msg, "\n - request {}:\n", request); - for m in mismatches { - let _ = writeln!(&mut msg, " - {}", m.description()); - } - } - MatchResult::RequestNotFound(request) => { - let _ = writeln!(&mut msg, "\n - received unexpected request {}:\n", short_description(&request)); - let debug_str = format!("{:#?}", request); - let _ = writeln!(&mut msg, "{}", debug_str.lines().map(|ln| format!(" {}", ln)).join("\n")); + Err(self.display_errors(mismatches)) + } + } + + #[cfg(feature = "colour")] + fn display_errors(&self, mismatches: Vec) -> String { + let size = termsize::get() + .map(|sz| if sz.cols > 2 { sz.cols - 2 } else { 0 }) + .unwrap_or(78); + let pad = "-".repeat(size as usize); + let mut msg = format!(" {} \nMock server {} failed verification:\n", pad, self.description.white().bold()); + for mismatch in mismatches { + match mismatch { + MatchResult::RequestMatch(..) => { + warn!("list of mismatches contains a match"); + } + MatchResult::RequestMismatch(request, _, mismatches) => { + let _ = writeln!(&mut msg, "\n - request {}:\n", request); + for m in mismatches { + let _ = writeln!(&mut msg, " - {}", m.description()); } - MatchResult::MissingRequest(request) => { - let _ = writeln!( - &mut msg, - "\n - request {} expected, but never occurred:\n", short_description(&request), - ); - let debug_str = format!("{:#?}", request); - let _ = writeln!(&mut msg, "{}", debug_str.lines().map(|ln| format!(" {}", ln)).join("\n")); + } + MatchResult::RequestNotFound(request) => { + let _ = writeln!(&mut msg, "\n - received unexpected request {}:\n", short_description(&request).white().bold()); + let debug_str = format!("{:#?}", request); + let debug_padded = debug_str.lines().map(|ln| format!(" {}", ln)).join("\n"); + let _ = writeln!(&mut msg, "{}", debug_padded.italic()); + } + MatchResult::MissingRequest(request) => { + let _ = writeln!( + &mut msg, + "\n - request {} expected, but never occurred:\n", short_description(&request).white().bold(), + ); + let debug_str = format!("{:#?}", request); + let debug_padded = debug_str.lines().map(|ln| format!(" {}", ln)).join("\n"); + let _ = writeln!(&mut msg, "{}", debug_padded.italic()); + } + } + } + let _ = writeln!(&mut msg, " {} ", pad); + msg + } + + #[cfg(not(feature = "colour"))] + fn display_errors(&self, mismatches: Vec) -> String { + let size = termsize::get() + .map(|sz| if sz.cols > 2 { sz.cols - 2 } else { 0 }) + .unwrap_or(78); + let pad = "-".repeat(size as usize); + let mut msg = format!(" {} \nMock server {} failed verification:\n", pad, self.description); + for mismatch in mismatches { + match mismatch { + MatchResult::RequestMatch(..) => { + warn!("list of mismatches contains a match"); + } + MatchResult::RequestMismatch(request, _, mismatches) => { + let _ = writeln!(&mut msg, "\n - request {}:\n", request); + for m in mismatches { + let _ = writeln!(&mut msg, " - {}", m.description()); } } + MatchResult::RequestNotFound(request) => { + let _ = writeln!(&mut msg, "\n - received unexpected request {}:\n", short_description(&request)); + let debug_str = format!("{:#?}", request); + let _ = writeln!(&mut msg, "{}", debug_str.lines().map(|ln| format!(" {}", ln)).join("\n")); + } + MatchResult::MissingRequest(request) => { + let _ = writeln!( + &mut msg, + "\n - request {} expected, but never occurred:\n", short_description(&request), + ); + let debug_str = format!("{:#?}", request); + let _ = writeln!(&mut msg, "{}", debug_str.lines().map(|ln| format!(" {}", ln)).join("\n")); + } } - let _ = writeln!(&mut msg, " {} ", pad); - Err(msg) } + let _ = writeln!(&mut msg, " {} ", pad); + msg } }