Skip to content

Commit

Permalink
fix: ensure the Pact has the correct interaction keys before sending …
Browse files Browse the repository at this point in the history
…verification request to a plugin
  • Loading branch information
rholshausen committed Jan 31, 2024
1 parent 47a8571 commit 1b54a4a
Show file tree
Hide file tree
Showing 5 changed files with 285 additions and 42 deletions.
89 changes: 60 additions & 29 deletions drivers/rust/driver/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions drivers/rust/driver/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ features = ["rustls-tls-native-roots", "json", "gzip", "deflate", "stream"]
env_logger = "0.11.0"
expectest = "0.12.0"
tempdir = "0.3.7"
test-log = "0.2.14"

[build-dependencies]
tonic-build = "0.10.2"
126 changes: 124 additions & 2 deletions drivers/rust/driver/src/plugin_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,18 @@ pub async fn prepare_validation_for_interaction(
let plugin = lookup_plugin(&manifest.as_dependency())
.ok_or_else(|| anyhow!("Did not find a running plugin for manifest {:?}", manifest))?;

prepare_validation_for_interaction_inner(&plugin, manifest, pact, interaction, context).await
}

pub(crate) async fn prepare_validation_for_interaction_inner(
plugin: &(dyn PactPluginRpc + Send + Sync),
manifest: &PactPluginManifest,
pact: &V4Pact,
interaction: &(dyn V4Interaction + Send + Sync),
context: &HashMap<String, Value>
) -> anyhow::Result<InteractionVerificationData> {
let mut pact = pact.clone();
pact.interactions = pact.interactions.iter().map(|i| i.with_unique_key()).collect();
let request = VerificationPreparationRequest {
pact: pact.to_json(PactSpecification::V4)?.to_string(),
interaction_key: interaction.unique_key(),
Expand Down Expand Up @@ -573,6 +585,26 @@ pub async fn verify_interaction(
let plugin = lookup_plugin(&manifest.as_dependency())
.ok_or_else(|| anyhow!("Did not find a running plugin for manifest {:?}", manifest))?;

verify_interaction_inner(
&plugin,
&manifest,
verification_data,
config,
pact,
interaction
).await
}

pub(crate) async fn verify_interaction_inner(
plugin: &(dyn PactPluginRpc + Send + Sync),
manifest: &PactPluginManifest,
verification_data: &InteractionVerificationData,
config: &HashMap<String, Value>,
pact: &V4Pact,
interaction: &(dyn V4Interaction + Send + Sync)
) -> anyhow::Result<InteractionVerificationResult> {
let mut pact = pact.clone();
pact.interactions = pact.interactions.iter().map(|i| i.with_unique_key()).collect();
let request = VerifyInteractionRequest {
pact: pact.to_json(PactSpecification::V4)?.to_string(),
interaction_key: interaction.unique_key(),
Expand Down Expand Up @@ -665,12 +697,26 @@ fn create_plugin_dir(manifest: &PactPluginManifest) -> anyhow::Result<PathBuf> {
mod tests {
use std::fs::{self, File};

use maplit::hashmap;
use pact_models::v4::sync_message::SynchronousMessage;
use pact_models::prelude::v4::V4Pact;
use pact_models::v4::interaction::V4Interaction;

use expectest::prelude::*;
use tempdir::TempDir;

use crate::plugin_models::PluginDependency;

use super::{load_manifest_from_dir, PactPluginManifest};
use crate::plugin_manager::prepare_validation_for_interaction_inner;
use crate::plugin_manager::verify_interaction_inner;
use crate::plugin_models::tests::MockPlugin;
use crate::plugin_models::tests::PREPARE_INTERACTION_FOR_VERIFICATION_ARG;
use crate::plugin_models::tests::VERIFY_INTERACTION_ARG;
use crate::verification::InteractionVerificationData;

use super::{
load_manifest_from_dir,
PactPluginManifest
};

#[test]
fn load_manifest_from_dir_test() {
Expand Down Expand Up @@ -735,4 +781,80 @@ mod tests {
let result = load_manifest_from_dir(&dep, &tmp_dir.path().to_path_buf()).unwrap();
expect!(result.version).to(be_equal_to("0.1.20"));
}

#[test_log::test(tokio::test)]
async fn prepare_validation_for_interaction_passes_in_pact_with_interaction_keys_set() {
let manifest = PactPluginManifest {
name: "test-plugin".to_string(),
version: "0.0.0".to_string(),
.. PactPluginManifest::default()
};
let mock_plugin = MockPlugin {
.. MockPlugin::default()
};

let interaction = SynchronousMessage {
.. SynchronousMessage::default()
};
let pact = V4Pact {
interactions: vec![ interaction.boxed_v4() ],
.. V4Pact::default()
};
let context = hashmap!{};

let result = prepare_validation_for_interaction_inner(
&mock_plugin,
&manifest,
&pact,
&interaction,
&context
).await;

expect!(result).to(be_ok());
let request = {
let r = PREPARE_INTERACTION_FOR_VERIFICATION_ARG.read().unwrap();
r.clone()
}.unwrap();
let pact_in = V4Pact::pact_from_json(&serde_json::from_str(request.pact.as_str()).unwrap(), "").unwrap();
expect!(pact_in.interactions[0].key().unwrap()).to(be_equal_to(request.interaction_key));
}

#[test_log::test(tokio::test)]
async fn verify_interaction_passes_in_pact_with_interaction_keys_set() {
let manifest = PactPluginManifest {
name: "test-plugin".to_string(),
version: "0.0.0".to_string(),
.. PactPluginManifest::default()
};
let mock_plugin = MockPlugin {
.. MockPlugin::default()
};

let interaction = SynchronousMessage {
.. SynchronousMessage::default()
};
let pact = V4Pact {
interactions: vec![ interaction.boxed_v4() ],
.. V4Pact::default()
};
let context = hashmap!{};
let data = InteractionVerificationData::default();

let result = verify_interaction_inner(
&mock_plugin,
&manifest,
&data,
&context,
&pact,
&interaction
).await;

expect!(result).to(be_ok());
let request = {
let r = VERIFY_INTERACTION_ARG.read().unwrap();
r.clone()
}.unwrap();
let pact_in = V4Pact::pact_from_json(&serde_json::from_str(request.pact.as_str()).unwrap(), "").unwrap();
expect!(pact_in.interactions[0].key().unwrap()).to(be_equal_to(request.interaction_key));
}
}
Loading

0 comments on commit 1b54a4a

Please sign in to comment.