Skip to content

Commit

Permalink
feat(ffi): add add_text_comment
Browse files Browse the repository at this point in the history
While the addition of `set_comment` allows for the comments to be used
as per the Pact specification, it did not follow the original intent for
comments (see the discussion in pact-foundation/pact-compatibility-suite#8).

The new `add_text_comment` function allows comments to be added
repeatedly and appended into an array as intended in
pact-foundation/pact-specification#45.

Signed-off-by: JP-Ellis <[email protected]>
  • Loading branch information
JP-Ellis committed Mar 14, 2024
1 parent 7fe434b commit ccfd878
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 0 deletions.
64 changes: 64 additions & 0 deletions rust/pact_ffi/src/mock_server/handles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2171,6 +2171,10 @@ ffi_fn!{
/// Note that a `value` that deserialize to a JSON null will result in a
/// comment being added, with the value being the JSON null.
///
/// Note that the `text` key is special and is used by
/// [`pactffi_add_text_comment`] to append comments to the array of comments.
/// Overwriting the `"text"` key is allowed, but should be used with caution.
///
/// # Safety
///
/// The comments parameter must be a valid pointer to a NULL terminated UTF-8,
Expand Down Expand Up @@ -2227,6 +2231,66 @@ ffi_fn!{
}
}

ffi_fn!{
/// Add a text comment to the interaction.
///
/// * `interaction` - Interaction handle to set the comments for.
/// * `comment` - Comment value.
///
/// This function will return `true` if the comments were successfully
/// updated. The `comment` must be a valid UTF-8 null-terminated string.
///
/// Unlike [`pactffi_set_comment`], this function will always append the
/// comment to the array of comments under the `"text"` key.
///
/// If, for any reason, the `"text"` key is not present or the associated
/// value not an array, it will be created as/replaced by an array and the
/// comment will be appended to it.
///
/// # Safety
///
/// The comments parameter must be a valid pointer to a NULL terminated UTF-8,
/// or NULL if the comment is to be cleared.
fn pactffi_add_text_comment(interaction: InteractionHandle, comment: *const c_char) -> bool {
let comment = match convert_cstr("comment", comment) {
Some(comment) => comment,
None => {
error!("add_text_comment: Comment value is not valid (NULL or non-UTF-8)");
return Err(anyhow!("Comment value is not valid (NULL or non-UTF-8)"));
}
};

let ensure_append = |comments: &mut serde_json::Value| {
match comments {
serde_json::Value::Array(_) => {
comments.as_array_mut().unwrap().push(serde_json::Value::String(comment.to_string()));
},
_ => {
*comments = serde_json::Value::Array(vec![serde_json::Value::String(comment.to_string())]);
}
}
};

interaction.with_interaction(&|_, _, inner| {
if let Some(reqres) = inner.as_v4_http_mut() {
ensure_append(reqres.comments.entry("text".to_string()).or_insert(serde_json::Value::Array(vec![])));
Ok(())
} else if let Some(message) = inner.as_v4_async_message_mut() {
ensure_append(message.comments.entry("text".to_string()).or_insert(serde_json::Value::Array(vec![])));
Ok(())
} else if let Some(sync_message) = inner.as_v4_sync_message_mut() {
ensure_append(sync_message.comments.entry("text".to_string()).or_insert(serde_json::Value::Array(vec![])));
Ok(())
} else {
error!("Interaction is an unknown type, is {}", inner.type_of());
Err(anyhow!("Interaction is an unknown type, is {}", inner.type_of()))
}
}).unwrap_or(Err(anyhow!("Not value to unwrap"))).is_ok()
} {
false
}
}

fn convert_ptr_to_body(body: *const u8, size: size_t, content_type: Option<ContentType>) -> OptionalBody {
if body.is_null() {
OptionalBody::Null
Expand Down
41 changes: 41 additions & 0 deletions rust/pact_ffi/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ use pact_ffi::mock_server::handles::{
InteractionPart,
PactHandle,
pact_default_file_name,
pactffi_add_text_comment,
pactffi_free_pact_handle,
pactffi_given_with_params,
pactffi_message_expects_to_receive,
Expand Down Expand Up @@ -346,6 +347,46 @@ fn set_comment() {
});
}

#[test]
fn add_text_comment() {
let consumer_name = CString::new("consumer").unwrap();
let provider_name = CString::new("provider").unwrap();
let pact_handle = pactffi_new_pact(consumer_name.as_ptr(), provider_name.as_ptr());
let description = CString::new("set_comment").unwrap();
let interaction = pactffi_new_interaction(pact_handle, description.as_ptr());

let values = vec![
CString::new("foo").unwrap(),
CString::new("bar").unwrap(),
CString::new("hello").unwrap(),
CString::new("world").unwrap(),
];

assert!(pactffi_add_text_comment(interaction, values[0].as_ptr()));
interaction.with_interaction(&|_, _, i| {
let interaction = i.as_v4_http().unwrap();
assert_eq!(interaction.comments["text"], json!(["foo"]));
});

assert!(pactffi_add_text_comment(interaction, values[1].as_ptr()));
interaction.with_interaction(&|_, _, i| {
let interaction = i.as_v4_http().unwrap();
assert_eq!(interaction.comments["text"], json!(["foo", "bar"]));
});

assert!(pactffi_add_text_comment(interaction, values[2].as_ptr()));
interaction.with_interaction(&|_, _, i| {
let interaction = i.as_v4_http().unwrap();
assert_eq!(interaction.comments["text"], json!(["foo", "bar", "hello"]));
});

assert!(pactffi_add_text_comment(interaction, values[3].as_ptr()));
interaction.with_interaction(&|_, _, i| {
let interaction = i.as_v4_http().unwrap();
assert_eq!(interaction.comments["text"], json!(["foo", "bar", "hello", "world"]));
});
}

#[test_log::test]
#[allow(deprecated)]
fn http_consumer_feature_test() {
Expand Down

0 comments on commit ccfd878

Please sign in to comment.