diff --git a/rust/pact_ffi/src/mock_server/handles.rs b/rust/pact_ffi/src/mock_server/handles.rs index 235b5da64..706b2a0e6 100644 --- a/rust/pact_ffi/src/mock_server/handles.rs +++ b/rust/pact_ffi/src/mock_server/handles.rs @@ -2138,6 +2138,78 @@ ffi_fn!{ } } +ffi_fn!{ + /// Add a comment to the interaction. + /// + /// * `interaction` - Interaction handle to set the comments for. + /// * `key` - Key value + /// * `value` - Comment value. This may be any valid JSON value, or a NULL to + /// clear the comment. + /// + /// This function will return `true` if the comments were successfully + /// updated. Both `key` and `value` must be valid UTF-8 null-terminated + /// strings; or in the case of `value`, it may also be a NULL pointer in which + /// case the comment will be cleared. + /// + /// Note that a `value` that deserialize to a JSON null will result in a + /// comment being added, with the value being the JSON null. + /// + /// # 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_set_comment(interaction: InteractionHandle, key: *const c_char, value: *const c_char) -> bool { + let key = match convert_cstr("key", key) { + Some(key) => key, + None => { + error!("set_comments: Key value is not valid (NULL or non-UTF-8)"); + return Err(anyhow!("Key value is not valid (NULL or non-UTF-8)")); + } + }; + let value: Option = if value.is_null() { + None + } else { + match convert_cstr("value", value) { + Some(value) =>{ match serde_json::from_str(value) { + Ok(value) => Some(value), + Err(_) => Some(serde_json::Value::String(value.to_string())), + }}, + None => { + error!("set_comments: Value is not valid (non-UTF-8)"); + return Err(anyhow!("Value is not valid (non-UTF-8)")); + } + } + }; + + interaction.with_interaction(&|_, _, inner| { + if let Some(reqres) = inner.as_v4_http_mut() { + match &value { + Some(value) => reqres.comments.insert(key.to_string(), value.clone()), + None => reqres.comments.remove(key) + }; + Ok(()) + } else if let Some(message) = inner.as_v4_async_message_mut() { + match &value { + Some(value) => message.comments.insert(key.to_string(), value.clone()), + None => message.comments.remove(key) + }; + Ok(()) + } else if let Some(sync_message) = inner.as_v4_sync_message_mut() { + match &value { + Some(value) => sync_message.comments.insert(key.to_string(), value.clone()), + None => sync_message.comments.remove(key) + }; + 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) -> OptionalBody { if body.is_null() { OptionalBody::Null diff --git a/rust/pact_ffi/tests/tests.rs b/rust/pact_ffi/tests/tests.rs index 75a846411..b4e07b2d5 100644 --- a/rust/pact_ffi/tests/tests.rs +++ b/rust/pact_ffi/tests/tests.rs @@ -47,6 +47,7 @@ use pact_ffi::mock_server::handles::{ pactffi_new_pact, pactffi_pact_handle_write_file, pactffi_response_status, + pactffi_set_comment, pactffi_set_key, pactffi_set_pending, pactffi_upon_receiving, @@ -291,6 +292,60 @@ fn set_pending() { }); } +#[test] +fn set_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 key_int = CString::new("key_int").unwrap(); + let value_int = CString::new("1234").unwrap(); + let key_str = CString::new("key_str").unwrap(); + let value_str = CString::new("some string").unwrap(); + let key_bool = CString::new("key_bool").unwrap(); + let value_bool = CString::new("true").unwrap(); + let key_float = CString::new("key_float").unwrap(); + let value_float = CString::new("12.34").unwrap(); + let key_array = CString::new("key_array").unwrap(); + let value_array = CString::new("[1, 2, 3]").unwrap(); + let key_obj = CString::new("key_object").unwrap(); + let value_obj = CString::new("{\"key\": \"value\"}").unwrap(); + + assert!(pactffi_set_comment(interaction, key_int.as_ptr(), value_int.as_ptr())); + assert!(pactffi_set_comment(interaction, key_str.as_ptr(), value_str.as_ptr())); + assert!(pactffi_set_comment(interaction, key_bool.as_ptr(), value_bool.as_ptr())); + assert!(pactffi_set_comment(interaction, key_float.as_ptr(), value_float.as_ptr())); + assert!(pactffi_set_comment(interaction, key_array.as_ptr(), value_array.as_ptr())); + assert!(pactffi_set_comment(interaction, key_obj.as_ptr(), value_obj.as_ptr())); + + interaction.with_interaction(&|_, _, i| { + let interaction = i.as_v4_http().unwrap(); + assert_eq!(interaction.comments["key_int"], json!(1234)); + assert_eq!(interaction.comments["key_str"], json!("some string")); + assert_eq!(interaction.comments["key_bool"], json!(true)); + assert_eq!(interaction.comments["key_float"], json!(12.34)); + assert_eq!(interaction.comments["key_array"], json!([1, 2, 3])); + assert_eq!(interaction.comments["key_object"], json!({"key": "value"})); + }); + + assert!(pactffi_set_comment(interaction, key_int.as_ptr(), null())); + assert!(pactffi_set_comment(interaction, key_str.as_ptr(), null())); + assert!(pactffi_set_comment(interaction, key_bool.as_ptr(), null())); + assert!(pactffi_set_comment(interaction, key_float.as_ptr(), null())); + assert!(pactffi_set_comment(interaction, key_array.as_ptr(), null())); + assert!(pactffi_set_comment(interaction, key_obj.as_ptr(), null())); + + interaction.with_interaction(&|_, _, i| { + let interaction = i.as_v4_http().unwrap(); + assert_eq!( + interaction.comments, + hashmap!{} + ) + }); +} + #[test_log::test] #[allow(deprecated)] fn http_consumer_feature_test() {