Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add pactffi_message_with_metadata_v2 #343

Merged
merged 1 commit into from Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
61 changes: 61 additions & 0 deletions rust/pact_ffi/src/mock_server/handles.rs
Expand Up @@ -2137,6 +2137,67 @@ pub extern fn pactffi_message_with_metadata(message_handle: MessageHandle, key:
}
}

/// Adds expected metadata to the Message
///
/// * `key` - metadata key
/// * `value` - metadata value, supports JSON structures with matchers and generators
#[no_mangle]
pub extern fn pactffi_message_with_metadata_v2(message_handle: MessageHandle, key: *const c_char, value: *const c_char) {
if let Some(key) = convert_cstr("key", key) {
let value = convert_cstr("value", value).unwrap_or_default();

message_handle.with_message(&|_, inner, _| {
if let Some(message) = inner.as_v4_async_message_mut() {
let matching_rules = message.contents.matching_rules.add_category(Category::METADATA);
let generators = &mut message.contents.generators;
let value = match serde_json::from_str(value) {
Ok(json) => match json {
Value::Object(ref obj) => {
if let Some(matcher_type) = obj.get("pact:matcher:type") {
debug!("detected pact:matcher:type, will configure a matcher");
let matcher_type = json_to_string(matcher_type);
let attributes = Value::Object(obj.clone());
let rule = MatchingRule::create(matcher_type.as_str(), &attributes).unwrap();
matching_rules.add_rule(DocPath::new(key).unwrap(), rule.clone(), RuleLogic::And);
}
if let Some(gen) = obj.get("pact:generator:type") {
debug!("detected pact:generator:type, will configure a generators");
if let Some(generator) = Generator::from_map(&json_to_string(gen), obj) {
generators.add_generator_with_subcategory(&GeneratorCategory::METADATA, DocPath::new(key).unwrap(), generator);
}
}
match obj.get("value") {
Some(inner) => match inner {
Value::Array(_) => {
warn!("Array value is not supported");
Value::Null
}
Value::Object(_) => {
warn!("Object value is not supported");
Value::Null
}
_ => inner.clone()
},
None => Value::Null
}
},
Value::String(string) => Value::String(string.to_string()),
_ => {
warn!("Only support object or string");
Value::String(value.to_string())
}
},
Err(err) => {
warn!("Failed to parse metadata from JSON - {}", err);
Value::String(value.to_string())
}
};
message.contents.metadata.insert(key.to_string(), value);
}
});
}
}

/// Reifies the given message
///
/// Reification is the process of stripping away any matchers, and returning the original contents.
Expand Down
31 changes: 31 additions & 0 deletions rust/pact_ffi/tests/tests.rs
Expand Up @@ -18,6 +18,7 @@ use reqwest::header::CONTENT_TYPE;
use tempfile::TempDir;
use serde_json::{json, Value};
use rstest::rstest;
use regex::Regex;

#[allow(deprecated)]
use pact_ffi::mock_server::{
Expand All @@ -35,6 +36,7 @@ use pact_ffi::mock_server::handles::{
pactffi_message_reify,
pactffi_message_with_contents,
pactffi_message_with_metadata,
pactffi_message_with_metadata_v2,
pactffi_new_interaction,
pactffi_new_message,
pactffi_new_message_pact,
Expand Down Expand Up @@ -409,6 +411,35 @@ fn message_xml_consumer_feature_test() {
expect!(res).to(be_eq(0));
}

#[test]
fn message_consumer_with_matchers_and_generators_test() {
let consumer_name = CString::new("message-consumer").unwrap();
let provider_name = CString::new("message-provider").unwrap();
let description = CString::new("message_request_with_matchers_and_generators").unwrap();
let content_type = CString::new("application/json").unwrap();
let metadata_key = CString::new("message-queue-name").unwrap();
let metadata_val = CString::new("{\"pact:generator:type\":\"RandomString\",\"value\":\"some text\",\"pact:matcher:type\":\"type\"}").unwrap();
let request_body_with_matchers = CString::new("{\"id\": {\"pact:generator:type\":\"RandomInt\",\"min\":1,\"pact:matcher:type\":\"integer\"}}").unwrap();
let file_path = CString::new("/tmp/pact").unwrap();
let given = CString::new("a functioning FFI interface").unwrap();
let receive_description = CString::new("a request to test the FFI interface").unwrap();

let message_pact_handle = pactffi_new_message_pact(consumer_name.as_ptr(), provider_name.as_ptr());
let message_handle = pactffi_new_message(message_pact_handle.clone(), description.as_ptr());
pactffi_message_given(message_handle.clone(), given.as_ptr());
pactffi_message_expects_to_receive(message_handle.clone(), receive_description.as_ptr());
let body_bytes = request_body_with_matchers.as_bytes();
pactffi_message_with_contents(message_handle.clone(), content_type.as_ptr(), body_bytes.as_ptr(), body_bytes.len());
pactffi_message_with_metadata_v2(message_handle.clone(), metadata_key.as_ptr(), metadata_val.as_ptr());
let res: *const c_char = pactffi_message_reify(message_handle.clone());
let reified = unsafe { CStr::from_ptr(res) }.to_str().unwrap();
let message = serde_json::from_str(reified).unwrap_or(json!({}));
expect!(Regex::new("\\d+").unwrap().is_match(message.get("contents").unwrap().get("id").unwrap().to_string().as_str())).to(be_true());
expect!(Regex::new("[\\d\\w]+").unwrap().is_match(message.get("metadata").unwrap().get("message-queue-name").unwrap().to_string().as_str())).to(be_true());
let res = pactffi_write_message_pact_file(message_pact_handle.clone(), file_path.as_ptr(), true);
expect!(res).to(be_eq(0));
}

#[test]
fn pactffi_verifier_cli_args_test() {
let data = pactffi_verifier_cli_args();
Expand Down