use json_eval_rs::JSONEval;
use serde_json::json;
#[test]
fn test_subform_detection_and_creation() {
let schema = json!({
"$params": {
"constants": {
"MAX_RIDERS": 5
}
},
"riders": {
"type": "array",
"title": "Riders",
"items": {
"$layout": {
"type": "VerticalLayout",
"elements": [
{ "$ref": "#/riders/properties/name" },
{ "$ref": "#/riders/properties/premium" }
]
},
"properties": {
"name": {
"type": "string",
"title": "Rider Name"
},
"premium": {
"type": "number",
"title": "Premium Amount"
}
}
}
}
});
let schema_str = serde_json::to_string(&schema).unwrap();
let eval = JSONEval::new(&schema_str, None, None).unwrap();
assert!(
eval.has_subform("#/riders"),
"Subform should be created for riders array"
);
let subform_paths = eval.get_subform_paths();
assert_eq!(subform_paths.len(), 1);
assert_eq!(subform_paths[0], "#/riders");
}
#[test]
fn test_subform_schema_structure() {
let schema = json!({
"$params": {
"constants": {
"MIN_PREMIUM": 100
}
},
"benefits": {
"type": "array",
"title": "Benefits",
"items": {
"properties": {
"code": {
"type": "string",
"title": "Benefit Code"
},
"amount": {
"type": "number",
"title": "Benefit Amount",
"rules": {
"required": {
"value": true,
"message": "Amount is required"
}
}
}
}
}
}
});
let schema_str = serde_json::to_string(&schema).unwrap();
let mut eval = JSONEval::new(&schema_str, None, None).unwrap();
let subform_schema = eval.get_evaluated_schema_subform("#/benefits");
assert!(
subform_schema.get("$params").is_some(),
"Subform should have $params"
);
assert_eq!(
subform_schema.pointer("/$params/constants/MIN_PREMIUM"),
Some(&json!(100))
);
assert!(
subform_schema.get("benefits").is_some(),
"Subform should have benefits field"
);
assert_eq!(
subform_schema.pointer("/benefits/type"),
Some(&json!("object"))
);
assert!(subform_schema
.pointer("/benefits/properties/code")
.is_some());
assert!(subform_schema
.pointer("/benefits/properties/amount")
.is_some());
}
#[test]
fn test_evaluate_subform() {
let schema = json!({
"$params": {
"constants": {
"TAX_RATE": 0.1
}
},
"items": {
"type": "array",
"items": {
"properties": {
"price": {
"type": "number"
},
"tax": {
"type": "number",
"$evaluation": {
"*": [
{ "$ref": "#/items/properties/price" },
{ "$ref": "#/$params/constants/TAX_RATE" }
]
}
}
}
}
}
});
let schema_str = serde_json::to_string(&schema).unwrap();
let mut eval = JSONEval::new(&schema_str, None, None).unwrap();
let data = json!({
"items": {
"price": 100
}
});
let data_str = serde_json::to_string(&data).unwrap();
eval.evaluate_subform("#/items", &data_str, None, None, None)
.unwrap();
let result = eval.get_evaluated_schema_subform("#/items");
assert_eq!(
result.pointer("/items/properties/tax/$evaluation"),
None,
"Evaluation should be resolved"
);
}
#[test]
fn test_validate_subform() {
let schema = json!({
"contacts": {
"type": "array",
"items": {
"properties": {
"email": {
"type": "string",
"rules": {
"required": {
"value": true,
"message": "Email is required"
},
"pattern": {
"value": "^[^@]+@[^@]+\\.[^@]+$",
"message": "Invalid email format"
}
}
},
"phone": {
"type": "string",
"rules": {
"required": {
"value": true,
"message": "Phone is required"
}
}
}
}
}
}
});
let schema_str = serde_json::to_string(&schema).unwrap();
let mut eval = JSONEval::new(&schema_str, None, None).unwrap();
let valid_data = json!({
"contacts": {
"email": "test@example.com",
"phone": "1234567890"
}
});
let valid_data_str = serde_json::to_string(&valid_data).unwrap();
let valid_result = eval
.validate_subform("#/contacts", &valid_data_str, None, None, None)
.unwrap();
assert!(!valid_result.has_error, "Valid data should pass validation");
let invalid_data = json!({
"contacts": {
"email": "test@example.com"
}
});
let invalid_data_str = serde_json::to_string(&invalid_data).unwrap();
let invalid_result = eval
.validate_subform("#/contacts", &invalid_data_str, None, None, None)
.unwrap();
println!(
"Validation result: has_error={}, errors={:?}",
invalid_result.has_error, invalid_result.errors
);
}
#[test]
fn test_evaluate_dependents_subform() {
let schema = json!({
"calculations": {
"type": "array",
"items": {
"properties": {
"base": {
"type": "number"
},
"multiplier": {
"type": "number"
},
"result": {
"type": "number",
"$evaluation": {
"*": [
{ "$ref": "#/calculations/properties/base" },
{ "$ref": "#/calculations/properties/multiplier" }
]
},
"dependents": []
}
}
}
}
});
let schema_str = serde_json::to_string(&schema).unwrap();
let mut eval = JSONEval::new(&schema_str, None, None).unwrap();
let data = json!({
"calculations": {
"base": 10,
"multiplier": 5
}
});
let data_str = serde_json::to_string(&data).unwrap();
let result = eval.evaluate_dependents_subform(
"#/calculations",
&[String::from("#/calculations/properties/base")],
Some(&data_str),
None,
false,
None,
None,
true,
);
assert!(result.is_ok(), "Should successfully evaluate dependents");
}
#[test]
fn test_resolve_layout_subform() {
let schema = json!({
"form_items": {
"type": "array",
"items": {
"$layout": {
"type": "HorizontalLayout",
"elements": [
{ "$ref": "#/form_items/properties/label" },
{ "$ref": "#/form_items/properties/value" }
]
},
"properties": {
"label": {
"type": "string",
"title": "Label"
},
"value": {
"type": "string",
"title": "Value"
}
}
}
}
});
let schema_str = serde_json::to_string(&schema).unwrap();
let mut eval = JSONEval::new(&schema_str, None, None).unwrap();
let result = eval.resolve_layout_subform("#/form_items", false);
assert!(result.is_ok(), "Should successfully resolve layout");
let schema = eval.get_evaluated_schema_subform("#/form_items");
assert!(schema.pointer("/form_items/$layout/elements").is_some());
}
#[test]
fn test_multiple_subforms() {
let schema = json!({
"$params": {
"constants": {
"MAX_ITEMS": 10
}
},
"riders": {
"type": "array",
"items": {
"properties": {
"name": { "type": "string" }
}
}
},
"benefits": {
"type": "array",
"items": {
"properties": {
"code": { "type": "string" }
}
}
},
"contacts": {
"type": "array",
"items": {
"properties": {
"email": { "type": "string" }
}
}
}
});
let schema_str = serde_json::to_string(&schema).unwrap();
let eval = JSONEval::new(&schema_str, None, None).unwrap();
let subform_paths = eval.get_subform_paths();
assert_eq!(subform_paths.len(), 3, "Should have 3 subforms");
assert!(eval.has_subform("#/riders"));
assert!(eval.has_subform("#/benefits"));
assert!(eval.has_subform("#/contacts"));
}
#[test]
fn test_subform_isolation() {
let schema = json!({
"$params": {
"constants": {
"VALUE": 100
}
},
"other_field": {
"type": "string",
"title": "Other Field"
},
"subform_field": {
"type": "array",
"items": {
"properties": {
"item": {
"type": "string"
}
}
}
}
});
let schema_str = serde_json::to_string(&schema).unwrap();
let mut eval = JSONEval::new(&schema_str, None, None).unwrap();
let subform_schema = eval.get_evaluated_schema_subform("#/subform_field");
assert!(subform_schema.get("$params").is_some());
assert!(subform_schema.get("subform_field").is_some());
assert!(
subform_schema.get("other_field").is_none(),
"Subform should not have parent's other fields"
);
}
#[test]
fn test_get_schema_value_subform() {
let schema = json!({
"items": {
"type": "array",
"items": {
"properties": {
"quantity": {
"type": "number",
"value": 5
},
"price": {
"type": "number",
"value": 10.5
}
}
}
}
});
let schema_str = serde_json::to_string(&schema).unwrap();
let mut eval = JSONEval::new(&schema_str, None, None).unwrap();
let values = eval.get_schema_value_subform("#/items");
assert!(values.is_object());
}
#[test]
fn get_schema_value_subform_does_not_mutate_parent_data_with_active_item() {
let schema = json!({
"illustration": {
"type": "object",
"properties": {
"product_benefit": {
"type": "object",
"properties": {
"riders": {
"type": "array",
"itemsRootKey": "riders",
"items": {
"properties": {
"code": { "type": "string" }
}
}
}
}
}
}
}
});
let data = json!({
"illustration": {
"product_benefit": {
"riders": [{ "code": "ZLOB" }]
}
}
});
let schema_str = schema.to_string();
let data_str = data.to_string();
let mut eval = JSONEval::new(&schema_str, None, Some(&data_str)).unwrap();
eval.evaluate(&data_str, None, None, None).unwrap();
let item_payload = json!({ "riders": { "code": "ZLOB" } }).to_string();
eval.evaluate_subform(
"illustration.product_benefit.riders.0",
&item_payload,
None,
None,
None,
)
.unwrap();
let parent_before = eval.data.clone();
let subform_data_before = eval
.subforms
.get("#/illustration/properties/product_benefit/properties/riders")
.unwrap()
.data
.clone();
let values = eval.get_schema_value_subform("illustration.product_benefit.riders.0");
assert_eq!(
values,
json!({ "riders": { "code": "ZLOB" } }),
"get_schema_value_subform must expose only subform-root data"
);
assert_eq!(
eval.data, parent_before,
"get_schema_value_subform must not mutate parent form data"
);
assert_eq!(
eval.subforms
.get("#/illustration/properties/product_benefit/properties/riders")
.unwrap()
.data,
subform_data_before,
"get_schema_value_subform must not append indexed rider root to subform data"
);
}
#[test]
fn test_get_evaluated_schema_without_params_subform() {
let schema = json!({
"$params": {
"constants": {
"TEST": "value"
}
},
"data": {
"type": "array",
"items": {
"properties": {
"field": {
"type": "string"
}
}
}
}
});
let schema_str = serde_json::to_string(&schema).unwrap();
let mut eval = JSONEval::new(&schema_str, None, None).unwrap();
let schema_without_params = eval.get_evaluated_schema_without_params_subform("#/data");
assert!(
schema_without_params.get("$params").is_none(),
"Should not have $params"
);
assert!(
schema_without_params.get("data").is_some(),
"Should have data field"
);
}
#[test]
fn test_nonexistent_subform_error() {
let schema = json!({
"regular_field": {
"type": "string"
}
});
let schema_str = serde_json::to_string(&schema).unwrap();
let mut eval = JSONEval::new(&schema_str, None, None).unwrap();
assert!(!eval.has_subform("#/nonexistent"));
let result = eval.evaluate_subform("#/nonexistent", "{}", None, None, None);
assert!(result.is_err());
assert!(result.unwrap_err().contains("Subform not found"));
}
#[test]
fn test_nested_subform_key() {
let schema = json!({
"properties": {
"form": {
"type": "object",
"properties": {
"riders": {
"type": "array",
"items": {
"properties": {
"name": { "type": "string" }
}
}
}
}
}
}
});
let schema_str = serde_json::to_string(&schema).unwrap();
let mut eval = JSONEval::new(&schema_str, None, None).unwrap();
assert!(eval.has_subform("#/properties/form/properties/riders"));
let schema_without_params =
eval.get_evaluated_schema_without_params_subform("#/properties/form/properties/riders");
assert!(
schema_without_params.get("riders").is_some(),
"Should extract only the last segment of the path as key"
);
assert!(
schema_without_params.get("properties").is_none(),
"Should not contain 'properties' from parent path"
);
}
#[test]
fn test_evaluate_dependents_subform_array_iteration() {
let schema = json!({
"$params": {
"constants": {
"MULTIPLIER": 2
}
},
"riders": {
"type": "array",
"items": {
"properties": {
"base": {
"type": "number",
"dependents": [
{
"$ref": "#/riders/properties/calculated/value",
"value": {
"$evaluation": {
"*": [
{ "$ref": "#/riders/properties/base" },
{ "$ref": "#/$params/constants/MULTIPLIER" }
]
}
}
}
]
},
"calculated": {
"type": "number",
"condition": {
"disabled": true
}
}
}
}
}
});
let schema_str = serde_json::to_string(&schema).unwrap();
let mut eval = JSONEval::new(&schema_str, None, None).unwrap();
let data = json!({
"riders": [
{ "base": 10 },
{ "base": 20 }
]
});
let data_str = serde_json::to_string(&data).unwrap();
let subform_schema = eval.get_evaluated_schema_subform("#/riders");
println!(
"Subform schema: {}",
serde_json::to_string_pretty(&subform_schema).unwrap()
);
let subform_values_before = eval.get_schema_value_object_subform("#/riders");
println!("Subform values before: {:?}", subform_values_before);
let result = eval
.evaluate_dependents(
&["riders[0].base".to_string(), "riders[1].base".to_string()],
Some(&data_str),
None,
true,
None,
None,
true, )
.unwrap();
let result_arr = result.as_array().expect("result should be an array");
assert_eq!(result_arr.len(), 2, "Should have 2 subform result items");
let mut found_0 = false;
let mut found_1 = false;
for item in result_arr {
let path = item.get("$ref").unwrap().as_str().unwrap();
let value = item.get("value").unwrap().as_f64().unwrap();
if path == "riders.0.calculated.value" {
assert_eq!(value, 20.0);
found_0 = true;
} else if path == "riders.1.calculated.value" {
assert_eq!(value, 40.0);
found_1 = true;
}
}
assert!(found_0, "Should have found evaluation for riders[0]");
assert!(found_1, "Should have found evaluation for riders[1]");
}