#![cfg(feature = "ffi")]
use json_eval_rs::ffi::*;
use serde_json::json;
use std::ffi::CString;
#[test]
fn test_ffi_methods_parity() {
let schema = json!({
"type": "object",
"properties": {
"name": {
"type": "string",
"options": ["A", "B"]
},
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": { "type": "string" }
}
},
"$layout": {
"type": "VerticalLayout",
"elements": [
{ "$ref": "#/items/properties/name" }
]
}
},
"form": {
"$layout": {
"type": "VerticalLayout",
"elements": [
{ "$ref": "name" }
]
}
}
}
});
let schema_str = CString::new(serde_json::to_string(&schema).unwrap()).unwrap();
unsafe {
let handle = json_eval_new(schema_str.as_ptr(), std::ptr::null(), std::ptr::null());
assert!(!handle.is_null(), "Handle should not be null");
let data = json!({
"items": [{"name": "Item 1"}]
});
let data_str = CString::new(serde_json::to_string(&data).unwrap()).unwrap();
let result = json_eval_evaluate(
handle,
data_str.as_ptr(),
std::ptr::null(),
std::ptr::null(),
);
assert!(
result.success,
"Evaluation should succeed: {}",
if result.error.is_null() {
"none"
} else {
std::ffi::CStr::from_ptr(result.error).to_str().unwrap()
}
);
json_eval_free_result(result);
let result = json_eval_get_evaluated_schema_resolved(handle);
assert!(
result.success,
"Get evaluated schema resolved should succeed"
);
assert!(result.data_len > 0, "Should return data");
let schema_bytes = std::slice::from_raw_parts(result.data_ptr, result.data_len);
let schema_val: serde_json::Value = serde_json::from_slice(schema_bytes).unwrap();
assert!(
schema_val
.pointer("/properties/form/$layout/elements/0/$fullpath")
.is_some(),
"$fullpath should be present in resolved layout"
);
assert_eq!(
schema_val
.pointer("/properties/form/$layout/elements/0/$fullpath")
.and_then(|v| v.as_str()),
Some("properties.name")
);
json_eval_free_result(result);
let result = json_eval_get_evaluated_schema_resolved_msgpack(handle);
assert!(
result.success,
"Get resolved evaluated schema MessagePack should succeed"
);
let resolved_msgpack = std::slice::from_raw_parts(result.data_ptr, result.data_len);
let resolved_msgpack_value: serde_json::Value = rmp_serde::from_slice(resolved_msgpack)
.expect("resolved MessagePack must decode into JSON value");
assert_eq!(resolved_msgpack_value, schema_val);
json_eval_free_result(result);
let result = json_eval_get_resolved_layout(handle);
assert!(result.success, "Get resolved layout should succeed");
assert!(result.data_len > 0, "Should return layout entries");
json_eval_free_result(result);
let field_path = CString::new("properties.name").unwrap();
let result = json_eval_get_field_options(handle, field_path.as_ptr());
assert!(result.success, "Get field options should succeed");
json_eval_free_result(result);
let subform_path = CString::new("#/items/0").unwrap();
let result = json_eval_get_resolved_layout_subform(handle, subform_path.as_ptr());
assert!(result.success, "Get resolved layout subform should succeed");
json_eval_free_result(result);
let result = json_eval_get_evaluated_schema_resolved_subform(handle, subform_path.as_ptr());
assert!(
result.success,
"Get evaluated schema resolved subform should succeed"
);
assert!(result.data_len > 0);
let subform_schema_bytes = std::slice::from_raw_parts(result.data_ptr, result.data_len);
let subform_schema: serde_json::Value =
serde_json::from_slice(subform_schema_bytes).unwrap();
assert!(
subform_schema
.pointer("/items/$layout/elements/0/$fullpath")
.is_some(),
"$fullpath should be present in subform resolved layout"
);
json_eval_free_result(result);
let v_schema = json!({
"type": "object",
"properties": {
"title": {
"type": "string",
"title": "Document Title",
"rules": { "required": { "value": true, "message": "Title is required" } }
},
"contacts": {
"type": "array",
"items": {
"properties": {
"phone": {
"type": "string",
"rules": { "required": { "value": true, "message": "Phone is required" } }
}
}
}
}
}
});
let v_schema_str = CString::new(serde_json::to_string(&v_schema).unwrap()).unwrap();
let v_handle = json_eval_new(v_schema_str.as_ptr(), std::ptr::null(), std::ptr::null());
let v_data_str = CString::new(r#"{"title":"","contacts":[{"phone":""}]}"#).unwrap();
let res_without = json_eval_validate(
v_handle,
v_data_str.as_ptr(),
std::ptr::null(),
false,
false,
);
assert!(res_without.success);
let res_without_val: serde_json::Value = serde_json::from_slice(
std::slice::from_raw_parts(res_without.data_ptr, res_without.data_len),
)
.unwrap();
assert_eq!(res_without_val["has_error"], true);
assert!(res_without_val["error"]["title"].is_object());
assert!(res_without_val["error"]["contacts.0.phone"].is_null());
json_eval_free_result(res_without);
let res_with =
json_eval_validate(v_handle, v_data_str.as_ptr(), std::ptr::null(), false, true);
assert!(res_with.success);
let res_with_val: serde_json::Value = serde_json::from_slice(std::slice::from_raw_parts(
res_with.data_ptr,
res_with.data_len,
))
.unwrap();
assert_eq!(res_with_val["has_error"], true);
assert!(res_with_val["error"]["title"].is_object());
assert_eq!(
res_with_val["error"]["title"]["data"]["title"],
"Document Title"
);
assert_eq!(res_with_val["error"]["title"]["data"]["required"], true);
assert!(res_with_val["error"]["contacts.0.phone"].is_object());
assert_eq!(
res_with_val["error"]["contacts.0.phone"]["code"],
"contacts.0.phone.required"
);
assert_eq!(
res_with_val["error"]["contacts.0.phone"]["data"]["required"],
true
);
json_eval_free_result(res_with);
json_eval_free(v_handle);
json_eval_free(handle);
}
}