Skip to content

Commit

Permalink
Merge pull request #343 from tienvx/message-with-metadata-v2
Browse files Browse the repository at this point in the history
feat: Add pactffi_message_with_metadata_v2
  • Loading branch information
rholshausen authored Nov 21, 2023
2 parents 078a52a + aec900a commit 6b84495
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 0 deletions.
61 changes: 61 additions & 0 deletions rust/pact_ffi/src/mock_server/handles.rs
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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

0 comments on commit 6b84495

Please sign in to comment.