Skip to content

Commit

Permalink
feat(pact_verifier): Allow provider state generator to fall back to t…
Browse files Browse the repository at this point in the history
…he provider state parameters #441
  • Loading branch information
rholshausen committed Jul 9, 2024
1 parent b52007e commit efa1295
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 5 deletions.
2 changes: 1 addition & 1 deletion rust/pact_matching/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ ansi_term = "0.12.1"
anyhow = "1.0.82"
base64 = "0.22.0"
bytes = { version = "1.6.0", features = ["serde"] }
chrono = { version = "0.4.38", features = ["std", "clock"], default_features = false, optional = true }
chrono = { version = "0.4.38", features = ["std", "clock"], default-features = false, optional = true }
difference = "2.0.0"
futures = "0.3.30"
hex = "0.4.3"
Expand Down
1 change: 1 addition & 0 deletions rust/pact_matching/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2056,6 +2056,7 @@ pub async fn match_sync_message_response<'a>(

/// Generates the request by applying any defined generators
// TODO: Need to pass in any plugin data
#[instrument(level = "trace")]
pub async fn generate_request(request: &HttpRequest, mode: &GeneratorTestMode, context: &HashMap<&str, Value>) -> HttpRequest {
trace!(?request, ?mode, ?context, "generate_request");
let mut request = request.clone();
Expand Down
3 changes: 2 additions & 1 deletion rust/pact_models/src/generators/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use rand::prelude::*;
#[cfg(target_family = "wasm")] use regex::{Captures, Regex};
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use tracing::{debug, trace, warn};
use tracing::{debug, instrument, trace, warn};
use uuid::Uuid;

use crate::bodies::OptionalBody;
Expand Down Expand Up @@ -768,6 +768,7 @@ macro_rules! generators {
}};
}

#[instrument(level = "trace")]
pub fn generate_value_from_context(expression: &str, context: &HashMap<&str, Value>, data_type: &Option<DataType>) -> anyhow::Result<DataValue> {
let result = if contains_expressions(expression) {
parse_expression(expression, &MapValueResolver { context: context.clone() })
Expand Down
13 changes: 11 additions & 2 deletions rust/pact_verifier/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -428,13 +428,22 @@ async fn verify_interaction<'a, F: RequestFilterExecutor, S: ProviderStateExecut
let context = execute_provider_states(interaction, provider_state_executor, &client, true)
.await
.map_err(|e| (e, vec![], start.elapsed()))?;
let provider_states_context = context
let mut provider_states_context = hashmap!{};
for provider_state in interaction.provider_states() {
for (k, v) in provider_state.params {
provider_states_context.insert(k, v);
}
}
for (k, v) in context {
provider_states_context.insert(k, v);
}
let provider_states_context = provider_states_context
.iter()
.map(|(k, v)| (k.as_str(), v.clone()))
.collect();

info!("Running provider verification for '{}'", interaction.description());
trace!("Interaction to verify: {:?}", interaction);
trace!(?provider_states_context, "Interaction to verify: {:?}", interaction);

#[allow(unused_assignments)] let mut result = Ok((None, vec![]));
#[cfg(feature = "plugins")]
Expand Down
96 changes: 95 additions & 1 deletion rust/pact_verifier/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use pact_models::sync_pact::RequestResponsePact;
use reqwest::Client;
use serde_json::{json, Value};

use pact_consumer::*;
use pact_consumer::{json_pattern, json_pattern_internal, like};
use pact_consumer::prelude::*;

use crate::{NullRequestFilterExecutor, PactSource, ProviderInfo, ProviderStateExecutor, ProviderTransport, publish_result, PublishOptions, VerificationOptions};
Expand Down Expand Up @@ -1042,3 +1042,97 @@ async fn fetch_pact_from_dir_filters_by_provider_name() {
let (pact, _, _, _) = first_result.unwrap();
expect!(pact.provider().name).to(be_equal_to(provider.name));
}

// Issue #441
#[test_log::test(tokio::test)]
async fn support_passing_provider_state_params_to_provider_state_generator() {
let server = PactBuilderAsync::new("RustPactVerifier", "441Provider")
.interaction("a request say hello to John", "", |mut i| async move {
i.request
.path("/api/hello/John");
i.response
.header("content-type", "application/json")
.json_body(json_pattern!({
"name": "John"
}));
i
})
.await
.start_mock_server(None);

#[allow(deprecated)]
let provider = ProviderInfo {
name: "provider_states_values".to_string(),
host: server.url().host_str().unwrap().to_string(),
port: Some(server.url().port().unwrap()),
transports: vec![
ProviderTransport {
transport: "HTTP".to_string(),
port: Some(server.url().port().unwrap()),
path: None,
scheme: Some("http".to_string())
}
],
.. ProviderInfo::default()
};

let verification_options = VerificationOptions::<NullRequestFilterExecutor> {
no_pacts_is_error: false,
.. VerificationOptions::default()
};
let provider_states = Arc::new(DummyProviderStateExecutor{});

let pact = RequestResponsePact::from_json("test", &json!({
"consumer": {
"name": "SomeConsumer"
},
"interactions": [
{
"description": "Hello John",
"providerStates": [
{
"name": "User exists",
"params": {
"name": "John"
}
}
],
"request": {
"generators": {
"path": {
"dataType": "STRING",
"expression": "/api/hello/${name}",
"type": "ProviderState"
}
},
"method": "GET",
"path": "/api/hello/James"
},
"response": {
"body": {
"name": "John"
},
"headers": {
"Content-Type": "application/json"
},
"status": 200
}
}
],
"metadata": {
"pact-jvm": {
"version": "4.6.7"
},
"pactSpecification": {
"version": "3.0.0"
}
},
"provider": {
"name": "SomeProvider"
}
})).unwrap();
let interaction = pact.interactions.first().unwrap();

let result = super::verify_interaction(&provider, interaction, &pact.boxed(), &verification_options, &provider_states).await;
expect!(result).to(be_ok());
}

0 comments on commit efa1295

Please sign in to comment.