From 2b5148b984fbdc25c1da66fd3ef5041f313f2162 Mon Sep 17 00:00:00 2001 From: Ronald Holshausen Date: Mon, 19 Feb 2024 06:44:24 +1100 Subject: [PATCH] feat(verifier): Add the client language to the verification results if set #307 --- rust/pact_verifier/src/lib.rs | 9 ++-- rust/pact_verifier/src/metrics.rs | 1 + rust/pact_verifier/src/pact_broker.rs | 66 ++++++++++++++++++++++----- rust/pact_verifier/src/tests.rs | 38 +++++++-------- 4 files changed, 81 insertions(+), 33 deletions(-) diff --git a/rust/pact_verifier/src/lib.rs b/rust/pact_verifier/src/lib.rs index 4094815c1..f5a5bb4b9 100644 --- a/rust/pact_verifier/src/lib.rs +++ b/rust/pact_verifier/src/lib.rs @@ -1074,7 +1074,7 @@ pub async fn verify_provider_async ) { let publish_result = match source { PactSource::BrokerUrl(_, broker_url, auth, links) => { publish_to_broker(results, source, &options.build_url, &options.provider_tags, &options.provider_branch, &options.provider_version, links.clone(), broker_url.clone(), - auth.clone() + auth.clone(), metrics_data ).await } PactSource::BrokerWithDynamicConfiguration { broker_url, auth, links, provider_branch, provider_tags, .. } => { publish_to_broker(results, source, &options.build_url, &provider_tags, &provider_branch, - &options.provider_version, links.clone(), broker_url.clone(), auth.clone() + &options.provider_version, links.clone(), broker_url.clone(), auth.clone(), metrics_data ).await } _ => { @@ -1600,6 +1601,7 @@ async fn publish_to_broker( links: Vec, broker_url: String, auth: Option, + metrics_data: Option<&VerificationMetrics> ) -> Result { info!("Publishing verification results back to the Pact Broker"); let result = if results.iter().all(|r| r.result.is_ok()) { @@ -1622,6 +1624,7 @@ async fn publish_to_broker( build_url.clone(), provider_tags.clone(), provider_branch.clone(), + metrics_data ).await } diff --git a/rust/pact_verifier/src/metrics.rs b/rust/pact_verifier/src/metrics.rs index 20ce8ed6e..3be34ef5b 100644 --- a/rust/pact_verifier/src/metrics.rs +++ b/rust/pact_verifier/src/metrics.rs @@ -1,6 +1,7 @@ //! Structs for collecting metrics for verification /// Metrics data to send after running a verification +#[derive(Clone, Debug)] pub struct VerificationMetrics { /// test framework used to run the tests pub test_framework: String, diff --git a/rust/pact_verifier/src/pact_broker.rs b/rust/pact_verifier/src/pact_broker.rs index 52f5332d2..cb85121e6 100644 --- a/rust/pact_verifier/src/pact_broker.rs +++ b/rust/pact_verifier/src/pact_broker.rs @@ -23,6 +23,7 @@ use tracing::{debug, error, info, trace, warn}; use pact_matching::Mismatch; use crate::{MismatchResult, VERIFIER_VERSION}; +use crate::metrics::VerificationMetrics; use crate::utils::with_retries; fn is_true(object: &serde_json::Map, field: &str) -> bool { @@ -790,7 +791,8 @@ pub async fn publish_verification_results( version: String, build_url: Option, provider_tags: Vec, - branch: Option + branch: Option, + metrics_data: Option<&VerificationMetrics> ) -> Result { let hal_client = HALClient::with_url(broker_url, auth.clone()); @@ -810,11 +812,16 @@ pub async fn publish_verification_results( "Response from the pact broker has no 'pb:publish-verification-results' link".into() ))?; - let json = build_payload(result, version, build_url); + let json = build_payload(result, version, build_url, metrics_data); hal_client.post_json(publish_link.href.unwrap_or_default().as_str(), json.to_string().as_str()).await } -fn build_payload(result: TestResult, version: String, build_url: Option) -> serde_json::Value { +fn build_payload( + result: TestResult, + version: String, + build_url: Option, + metrics_data: Option<&VerificationMetrics> +) -> serde_json::Value { let mut json = json!({ "success": result.to_bool(), "providerApplicationVersion": version, @@ -825,8 +832,16 @@ fn build_payload(result: TestResult, version: String, build_url: Option) }); let json_obj = json.as_object_mut().unwrap(); - if build_url.is_some() { - json_obj.insert("buildUrl".into(), json!(build_url.unwrap())); + if let Some(build_url) = build_url { + json_obj.insert("buildUrl".into(), build_url.into()); + } + + if let Some(metrics_data) = metrics_data { + json_obj.get_mut("verifiedBy").unwrap()["clientLanguage"] = json!({ + "testFramework": metrics_data.test_framework, + "name": metrics_data.app_name, + "version": metrics_data.app_version + }); } match result { @@ -1082,6 +1097,7 @@ mod tests { use pact_models::{Consumer, PactSpecification, Provider}; use pact_models::prelude::RequestResponsePact; use pact_models::sync_interaction::RequestResponseInteraction; + use pretty_assertions::assert_eq; use pact_consumer::*; use pact_consumer::prelude::*; @@ -2018,7 +2034,7 @@ mod tests { #[test] fn test_build_payload_with_success() { let result = TestResult::Ok(vec![]); - let payload = super::build_payload(result, "1".to_string(), None); + let payload = super::build_payload(result, "1".to_string(), None, None); expect!(payload).to(be_equal_to(json!({ "providerApplicationVersion": "1", "success": true, @@ -2033,7 +2049,7 @@ mod tests { #[test] fn test_build_payload_adds_the_build_url_if_provided() { let result = TestResult::Ok(vec![]); - let payload = super::build_payload(result, "1".to_string(), Some("http://build-url".to_string())); + let payload = super::build_payload(result, "1".to_string(), Some("http://build-url".to_string()), None); expect!(payload).to(be_equal_to(json!({ "providerApplicationVersion": "1", "success": true, @@ -2049,7 +2065,7 @@ mod tests { #[test] fn test_build_payload_adds_a_result_for_each_interaction() { let result = TestResult::Ok(vec![Some("1".to_string()), Some("2".to_string()), Some("3".to_string()), None]); - let payload = super::build_payload(result, "1".to_string(), Some("http://build-url".to_string())); + let payload = super::build_payload(result, "1".to_string(), Some("http://build-url".to_string()), None); expect!(payload).to(be_equal_to(json!({ "providerApplicationVersion": "1", "success": true, @@ -2069,7 +2085,7 @@ mod tests { #[test] fn test_build_payload_with_failure() { let result = TestResult::Failed(vec![]); - let payload = super::build_payload(result, "1".to_string(), None); + let payload = super::build_payload(result, "1".to_string(), None, None); expect!(payload).to(be_equal_to(json!({ "providerApplicationVersion": "1", "success": false, @@ -2093,7 +2109,7 @@ mod tests { interaction_id: Some("1234abc".to_string()) })) ]); - let payload = super::build_payload(result, "1".to_string(), None); + let payload = super::build_payload(result, "1".to_string(), None, None); expect!(payload).to(be_equal_to(json!({ "providerApplicationVersion": "1", "success": false, @@ -2120,7 +2136,7 @@ mod tests { let result = TestResult::Failed(vec![ (Some("1234abc".to_string()), Some(MismatchResult::Error("Bang".to_string(), Some("1234abc".to_string())))) ]); - let payload = super::build_payload(result, "1".to_string(), None); + let payload = super::build_payload(result, "1".to_string(), None, None); expect!(payload).to(be_equal_to(json!({ "providerApplicationVersion": "1", "success": false, @@ -2156,7 +2172,7 @@ mod tests { (Some("12345678".to_string()), Some(MismatchResult::Error("Bang".to_string(), Some("1234abc".to_string())))), (Some("abc123".to_string()), None) ]); - let payload = super::build_payload(result, "1".to_string(), None); + let payload = super::build_payload(result, "1".to_string(), None, None); expect!(payload).to(be_equal_to(json!({ "providerApplicationVersion": "1", "success": false, @@ -2191,6 +2207,32 @@ mod tests { }))); } + #[test] + fn test_build_payload_adds_client_language_version_if_provided() { + let result = TestResult::Ok(vec![]); + let metrics = VerificationMetrics { + test_framework: "TEST".to_string(), + app_name: "TESTER".to_string(), + app_version: "1.2.3".to_string() + }; + let payload = super::build_payload(result, "1".to_string(), Some("http://build-url".to_string()), Some(&metrics)); + assert_eq!(payload, json!({ + "providerApplicationVersion": "1", + "success": true, + "buildUrl": "http://build-url", + "testResults": [], + "verifiedBy": { + "implementation": "Pact-Rust", + "version": VERIFIER_VERSION, + "clientLanguage": { + "testFramework": "TEST", + "name": "TESTER", + "version": "1.2.3" + } + } + })); + } + #[test] fn build_link_from_json() { let json = json!({ diff --git a/rust/pact_verifier/src/tests.rs b/rust/pact_verifier/src/tests.rs index 851bf6389..8d969cb06 100644 --- a/rust/pact_verifier/src/tests.rs +++ b/rust/pact_verifier/src/tests.rs @@ -273,7 +273,7 @@ fn publish_result_does_nothing_if_not_from_broker() { provider_tags: vec![], .. super::PublishOptions::default() }; - super::publish_result(&vec![], &PactSource::File("/tmp/test".into()), &options).await; + super::publish_result(&vec![], &PactSource::File("/tmp/test".into()), &options, None).await; }) }); expect!(server_response).to(be_err()); @@ -318,14 +318,15 @@ async fn publish_successful_result_to_broker() { let source = PactSource::BrokerUrl("Test".to_string(), server.url().to_string(), None, links.clone()); publish_result(&[VerificationInteractionResult { - interaction_id: Some("1".to_string()), - interaction_key: None, - description: "".to_string(), - interaction_description: "".to_string(), - result: Ok(()), - pending: false, - duration: Default::default(), - }], &source, &options).await; + interaction_id: Some("1".to_string()), + interaction_key: None, + description: "".to_string(), + interaction_description: "".to_string(), + result: Ok(()), + pending: false, + duration: Default::default(), + }], &source, &options, None + ).await; // Same publish but with dynamic configuration as pact source: let source = PactSource::BrokerWithDynamicConfiguration { @@ -340,14 +341,15 @@ async fn publish_successful_result_to_broker() { links }; super::publish_result(&[VerificationInteractionResult { - interaction_id: Some("1".to_string()), - interaction_key: None, - description: "".to_string(), - interaction_description: "".to_string(), - result: Ok(()), - pending: false, - duration: Default::default(), - }], &source, &options).await; + interaction_id: Some("1".to_string()), + interaction_key: None, + description: "".to_string(), + interaction_description: "".to_string(), + result: Ok(()), + pending: false, + duration: Default::default(), + }], &source, &options, None + ).await; } #[test] @@ -1023,7 +1025,7 @@ async fn test_publish_results_from_url_source_with_provider_branch() { }; let verification_result = vec![]; - publish_result(&verification_result, &source, &options).await; + publish_result(&verification_result, &source, &options, None).await; } #[test_log::test(tokio::test)]