diff --git a/core/json/json_operations.rs b/core/json/json_operations.rs index f00c4d20e..726508381 100644 --- a/core/json/json_operations.rs +++ b/core/json/json_operations.rs @@ -500,4 +500,94 @@ mod tests { let result = json_patch(&target, &patch).unwrap(); assert_eq!(result, create_json(r#"{"old":"new_value"}"#)); } + + #[test] + fn test_json_remove_empty_args() { + let args = vec![]; + assert_eq!(json_remove(&args).unwrap(), OwnedValue::Null); + } + + #[test] + fn test_json_remove_array_element() { + let args = vec![create_json(r#"[1,2,3,4,5]"#), create_text("$[2]")]; + + let result = json_remove(&args).unwrap(); + match result { + OwnedValue::Text(t) => assert_eq!(t.value.as_str(), "[1,2,4,5]"), + _ => panic!("Expected Text value"), + } + } + + #[test] + fn test_json_remove_multiple_paths() { + let args = vec![ + create_json(r#"{"a": 1, "b": 2, "c": 3}"#), + create_text("$.a"), + create_text("$.c"), + ]; + + let result = json_remove(&args).unwrap(); + match result { + OwnedValue::Text(t) => assert_eq!(t.value.as_str(), r#"{"b":2}"#), + _ => panic!("Expected Text value"), + } + } + + #[test] + fn test_json_remove_nested_paths() { + let args = vec![ + create_json(r#"{"a": {"b": {"c": 1, "d": 2}}}"#), + create_text("$.a.b.c"), + ]; + + let result = json_remove(&args).unwrap(); + match result { + OwnedValue::Text(t) => assert_eq!(t.value.as_str(), r#"{"a":{"b":{"d":2}}}"#), + _ => panic!("Expected Text value"), + } + } + + #[test] + fn test_json_remove_duplicate_keys() { + let args = vec![ + create_json(r#"{"a": 1, "a": 2, "a": 3}"#), + create_text("$.a"), + ]; + + let result = json_remove(&args).unwrap(); + match result { + OwnedValue::Text(t) => assert_eq!(t.value.as_str(), r#"{"a":2,"a":3}"#), + _ => panic!("Expected Text value"), + } + } + + #[test] + fn test_json_remove_invalid_path() { + let args = vec![ + create_json(r#"{"a": 1}"#), + OwnedValue::Integer(42), // Invalid path type + ]; + + assert!(json_remove(&args).is_err()); + } + + #[test] + fn test_json_remove_complex_case() { + let args = vec![ + create_json(r#"{"a":[1,2,3],"b":{"x":1,"x":2},"c":[{"y":1},{"y":2}]}"#), + create_text("$.a[1]"), + create_text("$.b.x"), + create_text("$.c[0].y"), + ]; + + let result = json_remove(&args).unwrap(); + match result { + OwnedValue::Text(t) => { + let value = t.value.as_str(); + assert!(value.contains(r#"[1,3]"#)); + assert!(value.contains(r#"{"x":2}"#)); + } + _ => panic!("Expected Text value"), + } + } } diff --git a/core/json/mod.rs b/core/json/mod.rs index 0d0baf64e..532382da6 100644 --- a/core/json/mod.rs +++ b/core/json/mod.rs @@ -241,6 +241,7 @@ pub fn json_extract(value: &OwnedValue, paths: &[OwnedValue]) -> crate::Result crate::Result { match extracted { + Val::Removed => Ok(OwnedValue::Null), Val::Null => Ok(OwnedValue::Null), Val::Float(f) => Ok(OwnedValue::Float(*f)), Val::Integer(i) => Ok(OwnedValue::Integer(*i)), @@ -1062,4 +1063,98 @@ mod tests { .contains("json_object requires an even number of values")), } } + + #[test] + fn test_find_target_array() { + let mut val = Val::Array(vec![ + Val::String("first".to_string()), + Val::String("second".to_string()), + ]); + let path = JsonPath { + elements: vec![PathElement::ArrayLocator(0)], + }; + + match find_target(&mut val, &path) { + Some(Target::Array(_, idx)) => assert_eq!(idx, 0), + _ => panic!("Expected Array target"), + } + } + + #[test] + fn test_find_target_negative_index() { + let mut val = Val::Array(vec![ + Val::String("first".to_string()), + Val::String("second".to_string()), + ]); + let path = JsonPath { + elements: vec![PathElement::ArrayLocator(-1)], + }; + + match find_target(&mut val, &path) { + Some(Target::Array(_, idx)) => assert_eq!(idx, 1), + _ => panic!("Expected Array target"), + } + } + + #[test] + fn test_find_target_object() { + let mut val = Val::Object(vec![("key".to_string(), Val::String("value".to_string()))]); + let path = JsonPath { + elements: vec![PathElement::Key("key".to_string())], + }; + + match find_target(&mut val, &path) { + Some(Target::Value(_)) => {} + _ => panic!("Expected Value target"), + } + } + + #[test] + fn test_find_target_removed() { + let mut val = Val::Object(vec![ + ("key".to_string(), Val::Removed), + ("key".to_string(), Val::String("value".to_string())), + ]); + let path = JsonPath { + elements: vec![PathElement::Key("key".to_string())], + }; + + match find_target(&mut val, &path) { + Some(Target::Value(val)) => assert!(matches!(val, Val::String(_))), + _ => panic!("Expected second value, not removed"), + } + } + + #[test] + fn test_mutate_json() { + let mut val = Val::Array(vec![Val::String("test".to_string())]); + let path = JsonPath { + elements: vec![PathElement::ArrayLocator(0)], + }; + + let result = mutate_json_by_path(&mut val, path, |target| match target { + Target::Array(arr, idx) => { + arr.remove(idx); + "removed" + } + _ => panic!("Expected Array target"), + }); + + assert_eq!(result, Some("removed")); + assert!(matches!(val, Val::Array(arr) if arr.is_empty())); + } + + #[test] + fn test_mutate_json_none() { + let mut val = Val::Array(vec![]); + let path = JsonPath { + elements: vec![PathElement::ArrayLocator(0)], + }; + + let result: Option<()> = mutate_json_by_path(&mut val, path, |_| { + panic!("Should not be called"); + }); + + assert_eq!(result, None); + } }