1use argentor_core::{ArgentorResult, ToolCall, ToolResult};
9use argentor_skills::skill::{Skill, SkillDescriptor};
10use async_trait::async_trait;
11use serde_json::{Map, Value};
12use std::cmp::Ordering;
13
14pub struct JsonQuerySkill {
19 descriptor: SkillDescriptor,
20}
21
22impl JsonQuerySkill {
23 pub fn new() -> Self {
25 Self {
26 descriptor: SkillDescriptor {
27 name: "json_query".to_string(),
28 description: "Manipulate and query JSON data. Supports get, set, delete, keys, \
29 values, length, flatten, merge, diff, filter, sort, pick, omit, \
30 type_of, and validate operations."
31 .to_string(),
32 parameters_schema: serde_json::json!({
33 "type": "object",
34 "properties": {
35 "operation": {
36 "type": "string",
37 "enum": [
38 "get", "set", "delete", "keys", "values", "length",
39 "flatten", "merge", "diff", "filter", "sort", "pick",
40 "omit", "type_of", "validate"
41 ],
42 "description": "The JSON operation to perform"
43 },
44 "data": {
45 "description": "The JSON value to operate on"
46 },
47 "path": {
48 "type": "string",
49 "description": "Dot-notation path (e.g. 'users.0.name')"
50 },
51 "value": {
52 "description": "Value to set (for 'set' operation) or compare (for 'filter')"
53 },
54 "other": {
55 "description": "Second JSON value (for 'merge' and 'diff')"
56 },
57 "key": {
58 "type": "string",
59 "description": "Key name for 'filter' and 'sort'"
60 },
61 "keys": {
62 "type": "array",
63 "items": { "type": "string" },
64 "description": "List of keys for 'pick' and 'omit'"
65 },
66 "operator": {
67 "type": "string",
68 "enum": ["eq", "ne", "gt", "lt", "gte", "lte", "contains"],
69 "description": "Comparison operator for 'filter'"
70 },
71 "order": {
72 "type": "string",
73 "enum": ["asc", "desc"],
74 "description": "Sort order (default: 'asc')"
75 },
76 "schema": {
77 "type": "object",
78 "description": "JSON schema for 'validate' (supports type and required)"
79 },
80 "prefix": {
81 "type": "string",
82 "description": "Key prefix for 'flatten'"
83 }
84 },
85 "required": ["operation"]
86 }),
87 required_capabilities: vec![],
88 requires_approval: false,
89 },
90 }
91 }
92}
93
94impl Default for JsonQuerySkill {
95 fn default() -> Self {
96 Self::new()
97 }
98}
99
100#[async_trait]
101impl Skill for JsonQuerySkill {
102 fn descriptor(&self) -> &SkillDescriptor {
103 &self.descriptor
104 }
105
106 async fn execute(&self, call: ToolCall) -> ArgentorResult<ToolResult> {
107 let args = &call.arguments;
108
109 let operation = match args.get("operation").and_then(Value::as_str) {
110 Some(op) => op,
111 None => {
112 return Ok(ToolResult::error(
113 &call.id,
114 "Missing required parameter: 'operation'",
115 ));
116 }
117 };
118
119 let result = match operation {
120 "get" => op_get(args),
121 "set" => op_set(args),
122 "delete" => op_delete(args),
123 "keys" => op_keys(args),
124 "values" => op_values(args),
125 "length" => op_length(args),
126 "flatten" => op_flatten(args),
127 "merge" => op_merge(args),
128 "diff" => op_diff(args),
129 "filter" => op_filter(args),
130 "sort" => op_sort(args),
131 "pick" => op_pick(args),
132 "omit" => op_omit(args),
133 "type_of" => op_type_of(args),
134 "validate" => op_validate(args),
135 _ => Err(format!("Unknown operation: '{operation}'")),
136 };
137
138 match result {
139 Ok(value) => Ok(ToolResult::success(&call.id, value.to_string())),
140 Err(msg) => Ok(ToolResult::error(&call.id, msg)),
141 }
142 }
143}
144
145fn require_data(args: &Value) -> Result<&Value, String> {
150 args.get("data")
151 .ok_or_else(|| "Missing required parameter: 'data'".to_string())
152}
153
154fn require_path(args: &Value) -> Result<&str, String> {
155 args.get("path")
156 .and_then(Value::as_str)
157 .ok_or_else(|| "Missing required parameter: 'path'".to_string())
158}
159
160fn resolve_path<'a>(root: &'a Value, path: &str) -> Option<&'a Value> {
166 if path.is_empty() {
167 return Some(root);
168 }
169 let mut current = root;
170 for segment in path.split('.') {
171 current = match current {
172 Value::Object(map) => map.get(segment)?,
173 Value::Array(arr) => {
174 let idx: usize = segment.parse().ok()?;
175 arr.get(idx)?
176 }
177 _ => return None,
178 };
179 }
180 Some(current)
181}
182
183fn set_at_path(root: &Value, path: &str, value: Value) -> Result<Value, String> {
186 let segments: Vec<&str> = path.split('.').collect();
187 if segments.is_empty() {
188 return Ok(value);
189 }
190 set_recursive(root, &segments, value)
191}
192
193fn set_recursive(current: &Value, segments: &[&str], value: Value) -> Result<Value, String> {
194 if segments.is_empty() {
195 return Ok(value);
196 }
197
198 let key = segments[0];
199 let rest = &segments[1..];
200
201 match current {
202 Value::Object(map) => {
203 let mut new_map = map.clone();
204 let child = map.get(key).unwrap_or(&Value::Null);
205 let new_child = if rest.is_empty() {
206 value
207 } else {
208 let default_child = if rest.first().is_some_and(|s| s.parse::<usize>().is_ok()) {
210 if child.is_null() {
211 Value::Array(vec![])
212 } else {
213 child.clone()
214 }
215 } else if child.is_null() {
216 Value::Object(Map::new())
217 } else {
218 child.clone()
219 };
220 set_recursive(&default_child, rest, value)?
221 };
222 new_map.insert(key.to_string(), new_child);
223 Ok(Value::Object(new_map))
224 }
225 Value::Array(arr) => {
226 let idx: usize = key
227 .parse()
228 .map_err(|_| format!("Expected array index, got '{key}'"))?;
229 let mut new_arr = arr.clone();
230 while new_arr.len() <= idx {
232 new_arr.push(Value::Null);
233 }
234 let child = &new_arr[idx];
235 let new_child = if rest.is_empty() {
236 value
237 } else {
238 let default_child = if child.is_null() {
239 Value::Object(Map::new())
240 } else {
241 child.clone()
242 };
243 set_recursive(&default_child, rest, value)?
244 };
245 new_arr[idx] = new_child;
246 Ok(Value::Array(new_arr))
247 }
248 _ => {
249 if let Ok(idx) = key.parse::<usize>() {
251 let mut arr = vec![Value::Null; idx + 1];
252 let new_child = if rest.is_empty() {
253 value
254 } else {
255 set_recursive(&Value::Object(Map::new()), rest, value)?
256 };
257 arr[idx] = new_child;
258 Ok(Value::Array(arr))
259 } else {
260 let mut map = Map::new();
261 let new_child = if rest.is_empty() {
262 value
263 } else {
264 set_recursive(&Value::Object(Map::new()), rest, value)?
265 };
266 map.insert(key.to_string(), new_child);
267 Ok(Value::Object(map))
268 }
269 }
270 }
271}
272
273fn delete_at_path(root: &Value, path: &str) -> Result<Value, String> {
275 let segments: Vec<&str> = path.split('.').collect();
276 if segments.is_empty() {
277 return Ok(Value::Null);
278 }
279 delete_recursive(root, &segments)
280}
281
282fn delete_recursive(current: &Value, segments: &[&str]) -> Result<Value, String> {
283 if segments.is_empty() {
284 return Ok(current.clone());
285 }
286
287 let key = segments[0];
288 let rest = &segments[1..];
289
290 match current {
291 Value::Object(map) => {
292 let mut new_map = map.clone();
293 if rest.is_empty() {
294 new_map.remove(key);
295 } else if let Some(child) = map.get(key) {
296 new_map.insert(key.to_string(), delete_recursive(child, rest)?);
297 }
298 Ok(Value::Object(new_map))
299 }
300 Value::Array(arr) => {
301 let idx: usize = key
302 .parse()
303 .map_err(|_| format!("Expected array index, got '{key}'"))?;
304 if idx >= arr.len() {
305 return Ok(current.clone());
306 }
307 let mut new_arr = arr.clone();
308 if rest.is_empty() {
309 new_arr.remove(idx);
310 } else {
311 new_arr[idx] = delete_recursive(&arr[idx], rest)?;
312 }
313 Ok(Value::Array(new_arr))
314 }
315 _ => Ok(current.clone()),
316 }
317}
318
319fn op_get(args: &Value) -> Result<Value, String> {
324 let data = require_data(args)?;
325 let path = require_path(args)?;
326 match resolve_path(data, path) {
327 Some(v) => Ok(serde_json::json!({ "value": v })),
328 None => Err(format!("Path '{path}' not found")),
329 }
330}
331
332fn op_set(args: &Value) -> Result<Value, String> {
333 let data = require_data(args)?;
334 let path = require_path(args)?;
335 let value = args
336 .get("value")
337 .ok_or_else(|| "Missing required parameter: 'value'".to_string())?
338 .clone();
339 let result = set_at_path(data, path, value)?;
340 Ok(serde_json::json!({ "result": result }))
341}
342
343fn op_delete(args: &Value) -> Result<Value, String> {
344 let data = require_data(args)?;
345 let path = require_path(args)?;
346 let result = delete_at_path(data, path)?;
347 Ok(serde_json::json!({ "result": result }))
348}
349
350fn op_keys(args: &Value) -> Result<Value, String> {
351 let data = require_data(args)?;
352 match data {
353 Value::Object(map) => {
354 let keys: Vec<&str> = map.keys().map(String::as_str).collect();
355 Ok(serde_json::json!({ "keys": keys }))
356 }
357 _ => Err("'keys' operation requires an object".to_string()),
358 }
359}
360
361fn op_values(args: &Value) -> Result<Value, String> {
362 let data = require_data(args)?;
363 match data {
364 Value::Object(map) => {
365 let vals: Vec<&Value> = map.values().collect();
366 Ok(serde_json::json!({ "values": vals }))
367 }
368 _ => Err("'values' operation requires an object".to_string()),
369 }
370}
371
372fn op_length(args: &Value) -> Result<Value, String> {
373 let data = require_data(args)?;
374 let len = match data {
375 Value::Array(arr) => arr.len(),
376 Value::Object(map) => map.len(),
377 Value::String(s) => s.len(),
378 _ => return Err("'length' requires an array, object, or string".to_string()),
379 };
380 Ok(serde_json::json!({ "length": len }))
381}
382
383fn op_flatten(args: &Value) -> Result<Value, String> {
384 let data = require_data(args)?;
385 let prefix = args
386 .get("prefix")
387 .and_then(Value::as_str)
388 .unwrap_or("")
389 .to_string();
390 match data {
391 Value::Object(_) => {
392 let mut result = Map::new();
393 flatten_value(data, &prefix, &mut result);
394 Ok(serde_json::json!({ "result": Value::Object(result) }))
395 }
396 _ => Err("'flatten' operation requires an object".to_string()),
397 }
398}
399
400fn flatten_value(value: &Value, prefix: &str, out: &mut Map<String, Value>) {
401 match value {
402 Value::Object(map) => {
403 for (k, v) in map {
404 let new_key = if prefix.is_empty() {
405 k.clone()
406 } else {
407 format!("{prefix}.{k}")
408 };
409 flatten_value(v, &new_key, out);
410 }
411 }
412 Value::Array(arr) => {
413 for (i, v) in arr.iter().enumerate() {
414 let new_key = if prefix.is_empty() {
415 i.to_string()
416 } else {
417 format!("{prefix}.{i}")
418 };
419 flatten_value(v, &new_key, out);
420 }
421 }
422 _ => {
423 out.insert(prefix.to_string(), value.clone());
424 }
425 }
426}
427
428fn op_merge(args: &Value) -> Result<Value, String> {
429 let data = require_data(args)?;
430 let other = args
431 .get("other")
432 .ok_or_else(|| "Missing required parameter: 'other'".to_string())?;
433 match (data, other) {
434 (Value::Object(_), Value::Object(_)) => {
435 let merged = deep_merge(data, other);
436 Ok(serde_json::json!({ "result": merged }))
437 }
438 _ => Err("'merge' operation requires two objects".to_string()),
439 }
440}
441
442fn deep_merge(base: &Value, overlay: &Value) -> Value {
443 match (base, overlay) {
444 (Value::Object(base_map), Value::Object(overlay_map)) => {
445 let mut result = base_map.clone();
446 for (k, v) in overlay_map {
447 let merged = if let Some(existing) = result.get(k) {
448 deep_merge(existing, v)
449 } else {
450 v.clone()
451 };
452 result.insert(k.clone(), merged);
453 }
454 Value::Object(result)
455 }
456 (_, overlay) => overlay.clone(),
457 }
458}
459
460fn op_diff(args: &Value) -> Result<Value, String> {
461 let data = require_data(args)?;
462 let other = args
463 .get("other")
464 .ok_or_else(|| "Missing required parameter: 'other'".to_string())?;
465 match (data, other) {
466 (Value::Object(a), Value::Object(b)) => {
467 let mut added = Map::new();
468 let mut removed = Map::new();
469 let mut changed = Map::new();
470
471 for (k, v) in b {
473 if !a.contains_key(k) {
474 added.insert(k.clone(), v.clone());
475 }
476 }
477
478 for (k, v) in a {
480 match b.get(k) {
481 None => {
482 removed.insert(k.clone(), v.clone());
483 }
484 Some(bv) if bv != v => {
485 changed.insert(
486 k.clone(),
487 serde_json::json!({
488 "from": v,
489 "to": bv
490 }),
491 );
492 }
493 _ => {}
494 }
495 }
496
497 Ok(serde_json::json!({
498 "added": Value::Object(added),
499 "removed": Value::Object(removed),
500 "changed": Value::Object(changed)
501 }))
502 }
503 _ => Err("'diff' operation requires two objects".to_string()),
504 }
505}
506
507fn op_filter(args: &Value) -> Result<Value, String> {
508 let data = require_data(args)?;
509 let key = args
510 .get("key")
511 .and_then(Value::as_str)
512 .ok_or_else(|| "Missing required parameter: 'key'".to_string())?;
513 let operator = args
514 .get("operator")
515 .and_then(Value::as_str)
516 .ok_or_else(|| "Missing required parameter: 'operator'".to_string())?;
517 let filter_value = args
518 .get("value")
519 .ok_or_else(|| "Missing required parameter: 'value'".to_string())?;
520
521 let arr = data
522 .as_array()
523 .ok_or_else(|| "'filter' operation requires an array of objects".to_string())?;
524
525 let filtered: Vec<&Value> = arr
526 .iter()
527 .filter(|item| {
528 let field = match item.get(key) {
529 Some(v) => v,
530 None => return false,
531 };
532 compare_values(field, operator, filter_value)
533 })
534 .collect();
535
536 Ok(serde_json::json!({ "result": filtered }))
537}
538
539fn compare_values(field: &Value, operator: &str, target: &Value) -> bool {
540 match operator {
541 "eq" => field == target,
542 "ne" => field != target,
543 "contains" => match (field, target) {
544 (Value::String(s), Value::String(t)) => s.contains(t.as_str()),
545 (Value::Array(arr), _) => arr.contains(target),
546 _ => false,
547 },
548 "gt" | "lt" | "gte" | "lte" => {
549 let ord = numeric_cmp(field, target);
550 matches!(
551 (operator, ord),
552 ("gt", Some(Ordering::Greater))
553 | ("lt", Some(Ordering::Less))
554 | ("gte", Some(Ordering::Greater | Ordering::Equal))
555 | ("lte", Some(Ordering::Less | Ordering::Equal))
556 )
557 }
558 _ => false,
559 }
560}
561
562fn numeric_cmp(a: &Value, b: &Value) -> Option<Ordering> {
563 let a_f = value_as_f64(a)?;
564 let b_f = value_as_f64(b)?;
565 a_f.partial_cmp(&b_f)
566}
567
568fn value_as_f64(v: &Value) -> Option<f64> {
569 match v {
570 Value::Number(n) => n.as_f64(),
571 Value::String(s) => s.parse::<f64>().ok(),
572 _ => None,
573 }
574}
575
576fn op_sort(args: &Value) -> Result<Value, String> {
577 let data = require_data(args)?;
578 let key = args.get("key").and_then(Value::as_str);
579 let order = args.get("order").and_then(Value::as_str).unwrap_or("asc");
580
581 let arr = data
582 .as_array()
583 .ok_or_else(|| "'sort' operation requires an array".to_string())?;
584
585 let mut sorted = arr.clone();
586
587 sorted.sort_by(|a, b| {
588 let va = key.map_or(a, |k| a.get(k).unwrap_or(&Value::Null));
589 let vb = key.map_or(b, |k| b.get(k).unwrap_or(&Value::Null));
590 let cmp = sort_cmp(va, vb);
591 if order == "desc" {
592 cmp.reverse()
593 } else {
594 cmp
595 }
596 });
597
598 Ok(serde_json::json!({ "result": sorted }))
599}
600
601fn sort_cmp(a: &Value, b: &Value) -> Ordering {
602 if let (Some(af), Some(bf)) = (value_as_f64(a), value_as_f64(b)) {
604 return af.partial_cmp(&bf).unwrap_or(Ordering::Equal);
605 }
606 let sa = value_as_sort_string(a);
608 let sb = value_as_sort_string(b);
609 sa.cmp(&sb)
610}
611
612fn value_as_sort_string(v: &Value) -> String {
613 match v {
614 Value::String(s) => s.clone(),
615 Value::Null => String::new(),
616 other => other.to_string(),
617 }
618}
619
620fn op_pick(args: &Value) -> Result<Value, String> {
621 let data = require_data(args)?;
622 let keys = args
623 .get("keys")
624 .and_then(Value::as_array)
625 .ok_or_else(|| "Missing required parameter: 'keys' (array of strings)".to_string())?;
626
627 let map = data
628 .as_object()
629 .ok_or_else(|| "'pick' operation requires an object".to_string())?;
630
631 let mut result = Map::new();
632 for k in keys {
633 if let Some(key_str) = k.as_str() {
634 if let Some(v) = map.get(key_str) {
635 result.insert(key_str.to_string(), v.clone());
636 }
637 }
638 }
639
640 Ok(serde_json::json!({ "result": Value::Object(result) }))
641}
642
643fn op_omit(args: &Value) -> Result<Value, String> {
644 let data = require_data(args)?;
645 let keys = args
646 .get("keys")
647 .and_then(Value::as_array)
648 .ok_or_else(|| "Missing required parameter: 'keys' (array of strings)".to_string())?;
649
650 let map = data
651 .as_object()
652 .ok_or_else(|| "'omit' operation requires an object".to_string())?;
653
654 let omit_set: Vec<&str> = keys.iter().filter_map(Value::as_str).collect();
655 let mut result = Map::new();
656 for (k, v) in map {
657 if !omit_set.contains(&k.as_str()) {
658 result.insert(k.clone(), v.clone());
659 }
660 }
661
662 Ok(serde_json::json!({ "result": Value::Object(result) }))
663}
664
665fn op_type_of(args: &Value) -> Result<Value, String> {
666 let data = require_data(args)?;
667 let type_name = match data {
668 Value::Null => "null",
669 Value::Bool(_) => "boolean",
670 Value::Number(_) => "number",
671 Value::String(_) => "string",
672 Value::Array(_) => "array",
673 Value::Object(_) => "object",
674 };
675 Ok(serde_json::json!({ "type": type_name }))
676}
677
678fn op_validate(args: &Value) -> Result<Value, String> {
679 let data = require_data(args)?;
680 let schema = args
681 .get("schema")
682 .ok_or_else(|| "Missing required parameter: 'schema'".to_string())?;
683
684 let mut errors: Vec<String> = Vec::new();
685
686 if let Some(expected_type) = schema.get("type").and_then(Value::as_str) {
688 let actual = match data {
689 Value::Null => "null",
690 Value::Bool(_) => "boolean",
691 Value::Number(_) => "number",
692 Value::String(_) => "string",
693 Value::Array(_) => "array",
694 Value::Object(_) => "object",
695 };
696 let type_ok = if expected_type == "integer" {
698 data.as_i64().is_some() || data.as_u64().is_some()
699 } else {
700 actual == expected_type
701 };
702 if !type_ok {
703 errors.push(format!("Expected type '{expected_type}', got '{actual}'"));
704 }
705 }
706
707 if let (Some(required), Some(obj)) = (
709 schema.get("required").and_then(Value::as_array),
710 data.as_object(),
711 ) {
712 for req in required {
713 if let Some(field) = req.as_str() {
714 if !obj.contains_key(field) {
715 errors.push(format!("Missing required field: '{field}'"));
716 }
717 }
718 }
719 }
720
721 if let (Some(props), Some(obj)) = (
723 schema.get("properties").and_then(Value::as_object),
724 data.as_object(),
725 ) {
726 for (prop_name, prop_schema) in props {
727 if let Some(field_value) = obj.get(prop_name) {
728 if let Some(expected_type) = prop_schema.get("type").and_then(Value::as_str) {
729 let actual = match field_value {
730 Value::Null => "null",
731 Value::Bool(_) => "boolean",
732 Value::Number(_) => "number",
733 Value::String(_) => "string",
734 Value::Array(_) => "array",
735 Value::Object(_) => "object",
736 };
737 let type_ok = if expected_type == "integer" {
738 field_value.as_i64().is_some() || field_value.as_u64().is_some()
739 } else {
740 actual == expected_type
741 };
742 if !type_ok {
743 errors.push(format!(
744 "Field '{prop_name}': expected type '{expected_type}', got '{actual}'"
745 ));
746 }
747 }
748 }
749 }
750 }
751
752 if let Some(arr) = data.as_array() {
754 if let Some(min) = schema.get("minItems").and_then(Value::as_u64) {
755 if (arr.len() as u64) < min {
756 errors.push(format!(
757 "Array length {} is less than minItems {min}",
758 arr.len()
759 ));
760 }
761 }
762 if let Some(max) = schema.get("maxItems").and_then(Value::as_u64) {
763 if (arr.len() as u64) > max {
764 errors.push(format!("Array length {} exceeds maxItems {max}", arr.len()));
765 }
766 }
767 }
768
769 let valid = errors.is_empty();
770 Ok(serde_json::json!({
771 "valid": valid,
772 "errors": errors
773 }))
774}
775
776#[cfg(test)]
781#[allow(clippy::unwrap_used, clippy::expect_used)]
782mod tests {
783 use super::*;
784 use serde_json::json;
785
786 fn make_call(args: Value) -> ToolCall {
787 ToolCall {
788 id: "test".to_string(),
789 name: "json_query".to_string(),
790 arguments: args,
791 }
792 }
793
794 async fn exec(args: Value) -> ToolResult {
795 let skill = JsonQuerySkill::new();
796 skill.execute(make_call(args)).await.unwrap()
797 }
798
799 fn parse_result(result: &ToolResult) -> Value {
800 serde_json::from_str(&result.content).unwrap()
801 }
802
803 #[test]
808 fn test_descriptor() {
809 let skill = JsonQuerySkill::new();
810 let desc = skill.descriptor();
811 assert_eq!(desc.name, "json_query");
812 assert!(desc.required_capabilities.is_empty());
813 assert!(desc.parameters_schema.is_object());
814 }
815
816 #[test]
817 fn test_default() {
818 let skill = JsonQuerySkill::default();
819 assert_eq!(skill.descriptor().name, "json_query");
820 }
821
822 #[tokio::test]
827 async fn test_missing_operation() {
828 let r = exec(json!({})).await;
829 assert!(r.is_error);
830 assert!(r.content.contains("operation"));
831 }
832
833 #[tokio::test]
834 async fn test_unknown_operation() {
835 let r = exec(json!({"operation": "foo"})).await;
836 assert!(r.is_error);
837 assert!(r.content.contains("Unknown operation"));
838 }
839
840 #[tokio::test]
845 async fn test_get_simple() {
846 let r = exec(json!({
847 "operation": "get",
848 "data": {"name": "Alice", "age": 30},
849 "path": "name"
850 }))
851 .await;
852 assert!(!r.is_error);
853 let v = parse_result(&r);
854 assert_eq!(v["value"], "Alice");
855 }
856
857 #[tokio::test]
858 async fn test_get_nested() {
859 let r = exec(json!({
860 "operation": "get",
861 "data": {"users": [{"name": "Bob"}, {"name": "Carol"}]},
862 "path": "users.1.name"
863 }))
864 .await;
865 assert!(!r.is_error);
866 let v = parse_result(&r);
867 assert_eq!(v["value"], "Carol");
868 }
869
870 #[tokio::test]
871 async fn test_get_array_index() {
872 let r = exec(json!({
873 "operation": "get",
874 "data": {"items": [10, 20, 30]},
875 "path": "items.2"
876 }))
877 .await;
878 assert!(!r.is_error);
879 let v = parse_result(&r);
880 assert_eq!(v["value"], 30);
881 }
882
883 #[tokio::test]
884 async fn test_get_not_found() {
885 let r = exec(json!({
886 "operation": "get",
887 "data": {"a": 1},
888 "path": "b"
889 }))
890 .await;
891 assert!(r.is_error);
892 assert!(r.content.contains("not found"));
893 }
894
895 #[tokio::test]
896 async fn test_get_missing_data() {
897 let r = exec(json!({
898 "operation": "get",
899 "path": "a"
900 }))
901 .await;
902 assert!(r.is_error);
903 assert!(r.content.contains("data"));
904 }
905
906 #[tokio::test]
907 async fn test_get_missing_path() {
908 let r = exec(json!({
909 "operation": "get",
910 "data": {"a": 1}
911 }))
912 .await;
913 assert!(r.is_error);
914 assert!(r.content.contains("path"));
915 }
916
917 #[tokio::test]
922 async fn test_set_existing_key() {
923 let r = exec(json!({
924 "operation": "set",
925 "data": {"name": "Alice"},
926 "path": "name",
927 "value": "Bob"
928 }))
929 .await;
930 assert!(!r.is_error);
931 let v = parse_result(&r);
932 assert_eq!(v["result"]["name"], "Bob");
933 }
934
935 #[tokio::test]
936 async fn test_set_new_key() {
937 let r = exec(json!({
938 "operation": "set",
939 "data": {"a": 1},
940 "path": "b",
941 "value": 2
942 }))
943 .await;
944 assert!(!r.is_error);
945 let v = parse_result(&r);
946 assert_eq!(v["result"]["a"], 1);
947 assert_eq!(v["result"]["b"], 2);
948 }
949
950 #[tokio::test]
951 async fn test_set_nested() {
952 let r = exec(json!({
953 "operation": "set",
954 "data": {"user": {"name": "Alice"}},
955 "path": "user.age",
956 "value": 30
957 }))
958 .await;
959 assert!(!r.is_error);
960 let v = parse_result(&r);
961 assert_eq!(v["result"]["user"]["name"], "Alice");
962 assert_eq!(v["result"]["user"]["age"], 30);
963 }
964
965 #[tokio::test]
966 async fn test_set_array_element() {
967 let r = exec(json!({
968 "operation": "set",
969 "data": {"items": [1, 2, 3]},
970 "path": "items.1",
971 "value": 99
972 }))
973 .await;
974 assert!(!r.is_error);
975 let v = parse_result(&r);
976 assert_eq!(v["result"]["items"][1], 99);
977 }
978
979 #[tokio::test]
980 async fn test_set_missing_value() {
981 let r = exec(json!({
982 "operation": "set",
983 "data": {"a": 1},
984 "path": "a"
985 }))
986 .await;
987 assert!(r.is_error);
988 assert!(r.content.contains("value"));
989 }
990
991 #[tokio::test]
996 async fn test_delete_key() {
997 let r = exec(json!({
998 "operation": "delete",
999 "data": {"a": 1, "b": 2},
1000 "path": "a"
1001 }))
1002 .await;
1003 assert!(!r.is_error);
1004 let v = parse_result(&r);
1005 assert!(v["result"].get("a").is_none());
1006 assert_eq!(v["result"]["b"], 2);
1007 }
1008
1009 #[tokio::test]
1010 async fn test_delete_nested() {
1011 let r = exec(json!({
1012 "operation": "delete",
1013 "data": {"user": {"name": "Alice", "age": 30}},
1014 "path": "user.age"
1015 }))
1016 .await;
1017 assert!(!r.is_error);
1018 let v = parse_result(&r);
1019 assert_eq!(v["result"]["user"]["name"], "Alice");
1020 assert!(v["result"]["user"].get("age").is_none());
1021 }
1022
1023 #[tokio::test]
1024 async fn test_delete_array_element() {
1025 let r = exec(json!({
1026 "operation": "delete",
1027 "data": {"items": [10, 20, 30]},
1028 "path": "items.1"
1029 }))
1030 .await;
1031 assert!(!r.is_error);
1032 let v = parse_result(&r);
1033 let items = v["result"]["items"].as_array().unwrap();
1034 assert_eq!(items.len(), 2);
1035 assert_eq!(items[0], 10);
1036 assert_eq!(items[1], 30);
1037 }
1038
1039 #[tokio::test]
1040 async fn test_delete_nonexistent() {
1041 let r = exec(json!({
1042 "operation": "delete",
1043 "data": {"a": 1},
1044 "path": "b"
1045 }))
1046 .await;
1047 assert!(!r.is_error);
1048 let v = parse_result(&r);
1049 assert_eq!(v["result"]["a"], 1);
1050 }
1051
1052 #[tokio::test]
1057 async fn test_keys() {
1058 let r = exec(json!({
1059 "operation": "keys",
1060 "data": {"c": 3, "a": 1, "b": 2}
1061 }))
1062 .await;
1063 assert!(!r.is_error);
1064 let v = parse_result(&r);
1065 let keys = v["keys"].as_array().unwrap();
1066 assert_eq!(keys.len(), 3);
1067 assert!(keys.contains(&json!("a")));
1069 assert!(keys.contains(&json!("b")));
1070 assert!(keys.contains(&json!("c")));
1071 }
1072
1073 #[tokio::test]
1074 async fn test_keys_not_object() {
1075 let r = exec(json!({
1076 "operation": "keys",
1077 "data": [1, 2, 3]
1078 }))
1079 .await;
1080 assert!(r.is_error);
1081 }
1082
1083 #[tokio::test]
1088 async fn test_values() {
1089 let r = exec(json!({
1090 "operation": "values",
1091 "data": {"a": 1, "b": 2}
1092 }))
1093 .await;
1094 assert!(!r.is_error);
1095 let v = parse_result(&r);
1096 let vals = v["values"].as_array().unwrap();
1097 assert_eq!(vals.len(), 2);
1098 assert!(vals.contains(&json!(1)));
1099 assert!(vals.contains(&json!(2)));
1100 }
1101
1102 #[tokio::test]
1103 async fn test_values_not_object() {
1104 let r = exec(json!({
1105 "operation": "values",
1106 "data": "string"
1107 }))
1108 .await;
1109 assert!(r.is_error);
1110 }
1111
1112 #[tokio::test]
1117 async fn test_length_array() {
1118 let r = exec(json!({
1119 "operation": "length",
1120 "data": [1, 2, 3, 4]
1121 }))
1122 .await;
1123 assert!(!r.is_error);
1124 let v = parse_result(&r);
1125 assert_eq!(v["length"], 4);
1126 }
1127
1128 #[tokio::test]
1129 async fn test_length_object() {
1130 let r = exec(json!({
1131 "operation": "length",
1132 "data": {"a": 1, "b": 2}
1133 }))
1134 .await;
1135 assert!(!r.is_error);
1136 let v = parse_result(&r);
1137 assert_eq!(v["length"], 2);
1138 }
1139
1140 #[tokio::test]
1141 async fn test_length_string() {
1142 let r = exec(json!({
1143 "operation": "length",
1144 "data": "hello"
1145 }))
1146 .await;
1147 assert!(!r.is_error);
1148 let v = parse_result(&r);
1149 assert_eq!(v["length"], 5);
1150 }
1151
1152 #[tokio::test]
1153 async fn test_length_invalid() {
1154 let r = exec(json!({
1155 "operation": "length",
1156 "data": 42
1157 }))
1158 .await;
1159 assert!(r.is_error);
1160 }
1161
1162 #[tokio::test]
1167 async fn test_flatten_simple() {
1168 let r = exec(json!({
1169 "operation": "flatten",
1170 "data": {"a": {"b": {"c": 1}}, "d": 2}
1171 }))
1172 .await;
1173 assert!(!r.is_error);
1174 let v = parse_result(&r);
1175 assert_eq!(v["result"]["a.b.c"], 1);
1176 assert_eq!(v["result"]["d"], 2);
1177 }
1178
1179 #[tokio::test]
1180 async fn test_flatten_with_prefix() {
1181 let r = exec(json!({
1182 "operation": "flatten",
1183 "data": {"x": 1},
1184 "prefix": "root"
1185 }))
1186 .await;
1187 assert!(!r.is_error);
1188 let v = parse_result(&r);
1189 assert_eq!(v["result"]["root.x"], 1);
1190 }
1191
1192 #[tokio::test]
1193 async fn test_flatten_with_array() {
1194 let r = exec(json!({
1195 "operation": "flatten",
1196 "data": {"items": [10, 20]}
1197 }))
1198 .await;
1199 assert!(!r.is_error);
1200 let v = parse_result(&r);
1201 assert_eq!(v["result"]["items.0"], 10);
1202 assert_eq!(v["result"]["items.1"], 20);
1203 }
1204
1205 #[tokio::test]
1206 async fn test_flatten_not_object() {
1207 let r = exec(json!({
1208 "operation": "flatten",
1209 "data": [1, 2]
1210 }))
1211 .await;
1212 assert!(r.is_error);
1213 }
1214
1215 #[tokio::test]
1220 async fn test_merge_simple() {
1221 let r = exec(json!({
1222 "operation": "merge",
1223 "data": {"a": 1, "b": 2},
1224 "other": {"b": 3, "c": 4}
1225 }))
1226 .await;
1227 assert!(!r.is_error);
1228 let v = parse_result(&r);
1229 assert_eq!(v["result"]["a"], 1);
1230 assert_eq!(v["result"]["b"], 3); assert_eq!(v["result"]["c"], 4);
1232 }
1233
1234 #[tokio::test]
1235 async fn test_merge_deep() {
1236 let r = exec(json!({
1237 "operation": "merge",
1238 "data": {"user": {"name": "Alice", "age": 30}},
1239 "other": {"user": {"age": 31, "email": "a@b.c"}}
1240 }))
1241 .await;
1242 assert!(!r.is_error);
1243 let v = parse_result(&r);
1244 assert_eq!(v["result"]["user"]["name"], "Alice");
1245 assert_eq!(v["result"]["user"]["age"], 31);
1246 assert_eq!(v["result"]["user"]["email"], "a@b.c");
1247 }
1248
1249 #[tokio::test]
1250 async fn test_merge_missing_other() {
1251 let r = exec(json!({
1252 "operation": "merge",
1253 "data": {"a": 1}
1254 }))
1255 .await;
1256 assert!(r.is_error);
1257 assert!(r.content.contains("other"));
1258 }
1259
1260 #[tokio::test]
1261 async fn test_merge_not_objects() {
1262 let r = exec(json!({
1263 "operation": "merge",
1264 "data": [1],
1265 "other": [2]
1266 }))
1267 .await;
1268 assert!(r.is_error);
1269 }
1270
1271 #[tokio::test]
1276 async fn test_diff() {
1277 let r = exec(json!({
1278 "operation": "diff",
1279 "data": {"a": 1, "b": 2, "c": 3},
1280 "other": {"b": 2, "c": 99, "d": 4}
1281 }))
1282 .await;
1283 assert!(!r.is_error);
1284 let v = parse_result(&r);
1285 assert_eq!(v["removed"]["a"], 1);
1287 assert_eq!(v["added"]["d"], 4);
1289 assert_eq!(v["changed"]["c"]["from"], 3);
1291 assert_eq!(v["changed"]["c"]["to"], 99);
1292 assert!(v["added"].get("b").is_none());
1294 assert!(v["removed"].get("b").is_none());
1295 assert!(v["changed"].get("b").is_none());
1296 }
1297
1298 #[tokio::test]
1299 async fn test_diff_identical() {
1300 let r = exec(json!({
1301 "operation": "diff",
1302 "data": {"a": 1},
1303 "other": {"a": 1}
1304 }))
1305 .await;
1306 assert!(!r.is_error);
1307 let v = parse_result(&r);
1308 assert!(v["added"].as_object().unwrap().is_empty());
1309 assert!(v["removed"].as_object().unwrap().is_empty());
1310 assert!(v["changed"].as_object().unwrap().is_empty());
1311 }
1312
1313 #[tokio::test]
1314 async fn test_diff_not_objects() {
1315 let r = exec(json!({
1316 "operation": "diff",
1317 "data": 1,
1318 "other": 2
1319 }))
1320 .await;
1321 assert!(r.is_error);
1322 }
1323
1324 #[tokio::test]
1329 async fn test_filter_eq() {
1330 let r = exec(json!({
1331 "operation": "filter",
1332 "data": [
1333 {"name": "Alice", "age": 30},
1334 {"name": "Bob", "age": 25},
1335 {"name": "Carol", "age": 30}
1336 ],
1337 "key": "age",
1338 "operator": "eq",
1339 "value": 30
1340 }))
1341 .await;
1342 assert!(!r.is_error);
1343 let v = parse_result(&r);
1344 let results = v["result"].as_array().unwrap();
1345 assert_eq!(results.len(), 2);
1346 assert_eq!(results[0]["name"], "Alice");
1347 assert_eq!(results[1]["name"], "Carol");
1348 }
1349
1350 #[tokio::test]
1351 async fn test_filter_gt() {
1352 let r = exec(json!({
1353 "operation": "filter",
1354 "data": [
1355 {"val": 10},
1356 {"val": 20},
1357 {"val": 30}
1358 ],
1359 "key": "val",
1360 "operator": "gt",
1361 "value": 15
1362 }))
1363 .await;
1364 assert!(!r.is_error);
1365 let v = parse_result(&r);
1366 let results = v["result"].as_array().unwrap();
1367 assert_eq!(results.len(), 2);
1368 }
1369
1370 #[tokio::test]
1371 async fn test_filter_lt() {
1372 let r = exec(json!({
1373 "operation": "filter",
1374 "data": [{"v": 1}, {"v": 5}, {"v": 10}],
1375 "key": "v",
1376 "operator": "lt",
1377 "value": 5
1378 }))
1379 .await;
1380 assert!(!r.is_error);
1381 let v = parse_result(&r);
1382 let results = v["result"].as_array().unwrap();
1383 assert_eq!(results.len(), 1);
1384 assert_eq!(results[0]["v"], 1);
1385 }
1386
1387 #[tokio::test]
1388 async fn test_filter_gte() {
1389 let r = exec(json!({
1390 "operation": "filter",
1391 "data": [{"v": 1}, {"v": 5}, {"v": 10}],
1392 "key": "v",
1393 "operator": "gte",
1394 "value": 5
1395 }))
1396 .await;
1397 assert!(!r.is_error);
1398 let v = parse_result(&r);
1399 assert_eq!(v["result"].as_array().unwrap().len(), 2);
1400 }
1401
1402 #[tokio::test]
1403 async fn test_filter_lte() {
1404 let r = exec(json!({
1405 "operation": "filter",
1406 "data": [{"v": 1}, {"v": 5}, {"v": 10}],
1407 "key": "v",
1408 "operator": "lte",
1409 "value": 5
1410 }))
1411 .await;
1412 assert!(!r.is_error);
1413 let v = parse_result(&r);
1414 assert_eq!(v["result"].as_array().unwrap().len(), 2);
1415 }
1416
1417 #[tokio::test]
1418 async fn test_filter_ne() {
1419 let r = exec(json!({
1420 "operation": "filter",
1421 "data": [{"v": 1}, {"v": 2}, {"v": 3}],
1422 "key": "v",
1423 "operator": "ne",
1424 "value": 2
1425 }))
1426 .await;
1427 assert!(!r.is_error);
1428 let v = parse_result(&r);
1429 assert_eq!(v["result"].as_array().unwrap().len(), 2);
1430 }
1431
1432 #[tokio::test]
1433 async fn test_filter_contains() {
1434 let r = exec(json!({
1435 "operation": "filter",
1436 "data": [
1437 {"name": "Alice"},
1438 {"name": "Bob"},
1439 {"name": "Alicia"}
1440 ],
1441 "key": "name",
1442 "operator": "contains",
1443 "value": "Ali"
1444 }))
1445 .await;
1446 assert!(!r.is_error);
1447 let v = parse_result(&r);
1448 assert_eq!(v["result"].as_array().unwrap().len(), 2);
1449 }
1450
1451 #[tokio::test]
1452 async fn test_filter_missing_key() {
1453 let r = exec(json!({
1454 "operation": "filter",
1455 "data": [{"a": 1}],
1456 "operator": "eq",
1457 "value": 1
1458 }))
1459 .await;
1460 assert!(r.is_error);
1461 assert!(r.content.contains("key"));
1462 }
1463
1464 #[tokio::test]
1465 async fn test_filter_not_array() {
1466 let r = exec(json!({
1467 "operation": "filter",
1468 "data": {"a": 1},
1469 "key": "a",
1470 "operator": "eq",
1471 "value": 1
1472 }))
1473 .await;
1474 assert!(r.is_error);
1475 }
1476
1477 #[tokio::test]
1482 async fn test_sort_numbers_asc() {
1483 let r = exec(json!({
1484 "operation": "sort",
1485 "data": [3, 1, 2]
1486 }))
1487 .await;
1488 assert!(!r.is_error);
1489 let v = parse_result(&r);
1490 let arr = v["result"].as_array().unwrap();
1491 assert_eq!(arr, &[json!(1), json!(2), json!(3)]);
1492 }
1493
1494 #[tokio::test]
1495 async fn test_sort_numbers_desc() {
1496 let r = exec(json!({
1497 "operation": "sort",
1498 "data": [3, 1, 2],
1499 "order": "desc"
1500 }))
1501 .await;
1502 assert!(!r.is_error);
1503 let v = parse_result(&r);
1504 let arr = v["result"].as_array().unwrap();
1505 assert_eq!(arr, &[json!(3), json!(2), json!(1)]);
1506 }
1507
1508 #[tokio::test]
1509 async fn test_sort_by_key() {
1510 let r = exec(json!({
1511 "operation": "sort",
1512 "data": [
1513 {"name": "Charlie", "age": 25},
1514 {"name": "Alice", "age": 30},
1515 {"name": "Bob", "age": 20}
1516 ],
1517 "key": "age"
1518 }))
1519 .await;
1520 assert!(!r.is_error);
1521 let v = parse_result(&r);
1522 let arr = v["result"].as_array().unwrap();
1523 assert_eq!(arr[0]["name"], "Bob");
1524 assert_eq!(arr[1]["name"], "Charlie");
1525 assert_eq!(arr[2]["name"], "Alice");
1526 }
1527
1528 #[tokio::test]
1529 async fn test_sort_by_key_desc() {
1530 let r = exec(json!({
1531 "operation": "sort",
1532 "data": [
1533 {"name": "A"},
1534 {"name": "C"},
1535 {"name": "B"}
1536 ],
1537 "key": "name",
1538 "order": "desc"
1539 }))
1540 .await;
1541 assert!(!r.is_error);
1542 let v = parse_result(&r);
1543 let arr = v["result"].as_array().unwrap();
1544 assert_eq!(arr[0]["name"], "C");
1545 assert_eq!(arr[1]["name"], "B");
1546 assert_eq!(arr[2]["name"], "A");
1547 }
1548
1549 #[tokio::test]
1550 async fn test_sort_not_array() {
1551 let r = exec(json!({
1552 "operation": "sort",
1553 "data": {"a": 1}
1554 }))
1555 .await;
1556 assert!(r.is_error);
1557 }
1558
1559 #[tokio::test]
1564 async fn test_pick() {
1565 let r = exec(json!({
1566 "operation": "pick",
1567 "data": {"a": 1, "b": 2, "c": 3},
1568 "keys": ["a", "c"]
1569 }))
1570 .await;
1571 assert!(!r.is_error);
1572 let v = parse_result(&r);
1573 assert_eq!(v["result"]["a"], 1);
1574 assert_eq!(v["result"]["c"], 3);
1575 assert!(v["result"].get("b").is_none());
1576 }
1577
1578 #[tokio::test]
1579 async fn test_pick_missing_key() {
1580 let r = exec(json!({
1581 "operation": "pick",
1582 "data": {"a": 1},
1583 "keys": ["a", "z"]
1584 }))
1585 .await;
1586 assert!(!r.is_error);
1587 let v = parse_result(&r);
1588 assert_eq!(v["result"]["a"], 1);
1589 assert!(v["result"].get("z").is_none());
1590 }
1591
1592 #[tokio::test]
1593 async fn test_pick_not_object() {
1594 let r = exec(json!({
1595 "operation": "pick",
1596 "data": [1, 2],
1597 "keys": ["a"]
1598 }))
1599 .await;
1600 assert!(r.is_error);
1601 }
1602
1603 #[tokio::test]
1604 async fn test_pick_missing_keys_param() {
1605 let r = exec(json!({
1606 "operation": "pick",
1607 "data": {"a": 1}
1608 }))
1609 .await;
1610 assert!(r.is_error);
1611 assert!(r.content.contains("keys"));
1612 }
1613
1614 #[tokio::test]
1619 async fn test_omit() {
1620 let r = exec(json!({
1621 "operation": "omit",
1622 "data": {"a": 1, "b": 2, "c": 3},
1623 "keys": ["b"]
1624 }))
1625 .await;
1626 assert!(!r.is_error);
1627 let v = parse_result(&r);
1628 assert_eq!(v["result"]["a"], 1);
1629 assert_eq!(v["result"]["c"], 3);
1630 assert!(v["result"].get("b").is_none());
1631 }
1632
1633 #[tokio::test]
1634 async fn test_omit_not_object() {
1635 let r = exec(json!({
1636 "operation": "omit",
1637 "data": 42,
1638 "keys": ["a"]
1639 }))
1640 .await;
1641 assert!(r.is_error);
1642 }
1643
1644 #[tokio::test]
1649 async fn test_type_of_string() {
1650 let r = exec(json!({"operation": "type_of", "data": "hello"})).await;
1651 assert!(!r.is_error);
1652 let v = parse_result(&r);
1653 assert_eq!(v["type"], "string");
1654 }
1655
1656 #[tokio::test]
1657 async fn test_type_of_number() {
1658 let r = exec(json!({"operation": "type_of", "data": 42})).await;
1659 assert!(!r.is_error);
1660 let v = parse_result(&r);
1661 assert_eq!(v["type"], "number");
1662 }
1663
1664 #[tokio::test]
1665 async fn test_type_of_boolean() {
1666 let r = exec(json!({"operation": "type_of", "data": true})).await;
1667 assert!(!r.is_error);
1668 let v = parse_result(&r);
1669 assert_eq!(v["type"], "boolean");
1670 }
1671
1672 #[tokio::test]
1673 async fn test_type_of_null() {
1674 let r = exec(json!({"operation": "type_of", "data": null})).await;
1675 assert!(!r.is_error);
1676 let v = parse_result(&r);
1677 assert_eq!(v["type"], "null");
1678 }
1679
1680 #[tokio::test]
1681 async fn test_type_of_array() {
1682 let r = exec(json!({"operation": "type_of", "data": [1, 2]})).await;
1683 assert!(!r.is_error);
1684 let v = parse_result(&r);
1685 assert_eq!(v["type"], "array");
1686 }
1687
1688 #[tokio::test]
1689 async fn test_type_of_object() {
1690 let r = exec(json!({"operation": "type_of", "data": {"a": 1}})).await;
1691 assert!(!r.is_error);
1692 let v = parse_result(&r);
1693 assert_eq!(v["type"], "object");
1694 }
1695
1696 #[tokio::test]
1701 async fn test_validate_valid() {
1702 let r = exec(json!({
1703 "operation": "validate",
1704 "data": {"name": "Alice", "age": 30},
1705 "schema": {
1706 "type": "object",
1707 "required": ["name", "age"],
1708 "properties": {
1709 "name": {"type": "string"},
1710 "age": {"type": "number"}
1711 }
1712 }
1713 }))
1714 .await;
1715 assert!(!r.is_error);
1716 let v = parse_result(&r);
1717 assert_eq!(v["valid"], true);
1718 assert!(v["errors"].as_array().unwrap().is_empty());
1719 }
1720
1721 #[tokio::test]
1722 async fn test_validate_wrong_type() {
1723 let r = exec(json!({
1724 "operation": "validate",
1725 "data": "not an object",
1726 "schema": {"type": "object"}
1727 }))
1728 .await;
1729 assert!(!r.is_error);
1730 let v = parse_result(&r);
1731 assert_eq!(v["valid"], false);
1732 let errors = v["errors"].as_array().unwrap();
1733 assert!(!errors.is_empty());
1734 }
1735
1736 #[tokio::test]
1737 async fn test_validate_missing_required() {
1738 let r = exec(json!({
1739 "operation": "validate",
1740 "data": {"name": "Alice"},
1741 "schema": {
1742 "type": "object",
1743 "required": ["name", "email"]
1744 }
1745 }))
1746 .await;
1747 assert!(!r.is_error);
1748 let v = parse_result(&r);
1749 assert_eq!(v["valid"], false);
1750 let errors: Vec<String> = v["errors"]
1751 .as_array()
1752 .unwrap()
1753 .iter()
1754 .map(|e| e.as_str().unwrap().to_string())
1755 .collect();
1756 assert!(errors.iter().any(|e| e.contains("email")));
1757 }
1758
1759 #[tokio::test]
1760 async fn test_validate_property_type_mismatch() {
1761 let r = exec(json!({
1762 "operation": "validate",
1763 "data": {"age": "thirty"},
1764 "schema": {
1765 "type": "object",
1766 "properties": {
1767 "age": {"type": "number"}
1768 }
1769 }
1770 }))
1771 .await;
1772 assert!(!r.is_error);
1773 let v = parse_result(&r);
1774 assert_eq!(v["valid"], false);
1775 }
1776
1777 #[tokio::test]
1778 async fn test_validate_integer_type() {
1779 let r = exec(json!({
1780 "operation": "validate",
1781 "data": 42,
1782 "schema": {"type": "integer"}
1783 }))
1784 .await;
1785 assert!(!r.is_error);
1786 let v = parse_result(&r);
1787 assert_eq!(v["valid"], true);
1788 }
1789
1790 #[tokio::test]
1791 async fn test_validate_array_min_items() {
1792 let r = exec(json!({
1793 "operation": "validate",
1794 "data": [1],
1795 "schema": {"type": "array", "minItems": 3}
1796 }))
1797 .await;
1798 assert!(!r.is_error);
1799 let v = parse_result(&r);
1800 assert_eq!(v["valid"], false);
1801 }
1802
1803 #[tokio::test]
1804 async fn test_validate_array_max_items() {
1805 let r = exec(json!({
1806 "operation": "validate",
1807 "data": [1, 2, 3, 4, 5],
1808 "schema": {"type": "array", "maxItems": 3}
1809 }))
1810 .await;
1811 assert!(!r.is_error);
1812 let v = parse_result(&r);
1813 assert_eq!(v["valid"], false);
1814 }
1815
1816 #[tokio::test]
1817 async fn test_validate_missing_schema() {
1818 let r = exec(json!({
1819 "operation": "validate",
1820 "data": {}
1821 }))
1822 .await;
1823 assert!(r.is_error);
1824 assert!(r.content.contains("schema"));
1825 }
1826
1827 #[tokio::test]
1832 async fn test_get_deeply_nested() {
1833 let r = exec(json!({
1834 "operation": "get",
1835 "data": {"a": {"b": {"c": {"d": {"e": 42}}}}},
1836 "path": "a.b.c.d.e"
1837 }))
1838 .await;
1839 assert!(!r.is_error);
1840 let v = parse_result(&r);
1841 assert_eq!(v["value"], 42);
1842 }
1843
1844 #[tokio::test]
1845 async fn test_set_creates_intermediate_objects() {
1846 let r = exec(json!({
1847 "operation": "set",
1848 "data": {},
1849 "path": "a.b.c",
1850 "value": "deep"
1851 }))
1852 .await;
1853 assert!(!r.is_error);
1854 let v = parse_result(&r);
1855 assert_eq!(v["result"]["a"]["b"]["c"], "deep");
1856 }
1857
1858 #[tokio::test]
1859 async fn test_flatten_empty_object() {
1860 let r = exec(json!({
1861 "operation": "flatten",
1862 "data": {}
1863 }))
1864 .await;
1865 assert!(!r.is_error);
1866 let v = parse_result(&r);
1867 assert!(v["result"].as_object().unwrap().is_empty());
1868 }
1869
1870 #[tokio::test]
1871 async fn test_filter_empty_array() {
1872 let r = exec(json!({
1873 "operation": "filter",
1874 "data": [],
1875 "key": "x",
1876 "operator": "eq",
1877 "value": 1
1878 }))
1879 .await;
1880 assert!(!r.is_error);
1881 let v = parse_result(&r);
1882 assert!(v["result"].as_array().unwrap().is_empty());
1883 }
1884
1885 #[tokio::test]
1886 async fn test_sort_empty_array() {
1887 let r = exec(json!({
1888 "operation": "sort",
1889 "data": []
1890 }))
1891 .await;
1892 assert!(!r.is_error);
1893 let v = parse_result(&r);
1894 assert!(v["result"].as_array().unwrap().is_empty());
1895 }
1896
1897 #[tokio::test]
1898 async fn test_sort_strings() {
1899 let r = exec(json!({
1900 "operation": "sort",
1901 "data": ["banana", "apple", "cherry"]
1902 }))
1903 .await;
1904 assert!(!r.is_error);
1905 let v = parse_result(&r);
1906 let arr = v["result"].as_array().unwrap();
1907 assert_eq!(arr[0], "apple");
1908 assert_eq!(arr[1], "banana");
1909 assert_eq!(arr[2], "cherry");
1910 }
1911
1912 #[tokio::test]
1913 async fn test_merge_empty_objects() {
1914 let r = exec(json!({
1915 "operation": "merge",
1916 "data": {},
1917 "other": {}
1918 }))
1919 .await;
1920 assert!(!r.is_error);
1921 let v = parse_result(&r);
1922 assert!(v["result"].as_object().unwrap().is_empty());
1923 }
1924
1925 #[tokio::test]
1926 async fn test_diff_empty_objects() {
1927 let r = exec(json!({
1928 "operation": "diff",
1929 "data": {},
1930 "other": {}
1931 }))
1932 .await;
1933 assert!(!r.is_error);
1934 let v = parse_result(&r);
1935 assert!(v["added"].as_object().unwrap().is_empty());
1936 assert!(v["removed"].as_object().unwrap().is_empty());
1937 assert!(v["changed"].as_object().unwrap().is_empty());
1938 }
1939
1940 #[tokio::test]
1941 async fn test_pick_empty_keys() {
1942 let r = exec(json!({
1943 "operation": "pick",
1944 "data": {"a": 1, "b": 2},
1945 "keys": []
1946 }))
1947 .await;
1948 assert!(!r.is_error);
1949 let v = parse_result(&r);
1950 assert!(v["result"].as_object().unwrap().is_empty());
1951 }
1952
1953 #[tokio::test]
1954 async fn test_omit_empty_keys() {
1955 let r = exec(json!({
1956 "operation": "omit",
1957 "data": {"a": 1, "b": 2},
1958 "keys": []
1959 }))
1960 .await;
1961 assert!(!r.is_error);
1962 let v = parse_result(&r);
1963 assert_eq!(v["result"]["a"], 1);
1964 assert_eq!(v["result"]["b"], 2);
1965 }
1966
1967 #[tokio::test]
1968 async fn test_filter_contains_array() {
1969 let r = exec(json!({
1970 "operation": "filter",
1971 "data": [
1972 {"tags": ["rust", "wasm"]},
1973 {"tags": ["python"]},
1974 {"tags": ["rust", "security"]}
1975 ],
1976 "key": "tags",
1977 "operator": "contains",
1978 "value": "rust"
1979 }))
1980 .await;
1981 assert!(!r.is_error);
1982 let v = parse_result(&r);
1983 assert_eq!(v["result"].as_array().unwrap().len(), 2);
1984 }
1985
1986 #[tokio::test]
1987 async fn test_validate_all_pass() {
1988 let r = exec(json!({
1989 "operation": "validate",
1990 "data": [1, 2, 3],
1991 "schema": {
1992 "type": "array",
1993 "minItems": 1,
1994 "maxItems": 5
1995 }
1996 }))
1997 .await;
1998 assert!(!r.is_error);
1999 let v = parse_result(&r);
2000 assert_eq!(v["valid"], true);
2001 }
2002
2003 #[tokio::test]
2004 async fn test_keys_empty_object() {
2005 let r = exec(json!({
2006 "operation": "keys",
2007 "data": {}
2008 }))
2009 .await;
2010 assert!(!r.is_error);
2011 let v = parse_result(&r);
2012 assert!(v["keys"].as_array().unwrap().is_empty());
2013 }
2014
2015 #[tokio::test]
2016 async fn test_values_empty_object() {
2017 let r = exec(json!({
2018 "operation": "values",
2019 "data": {}
2020 }))
2021 .await;
2022 assert!(!r.is_error);
2023 let v = parse_result(&r);
2024 assert!(v["values"].as_array().unwrap().is_empty());
2025 }
2026
2027 #[tokio::test]
2028 async fn test_length_empty_array() {
2029 let r = exec(json!({
2030 "operation": "length",
2031 "data": []
2032 }))
2033 .await;
2034 assert!(!r.is_error);
2035 let v = parse_result(&r);
2036 assert_eq!(v["length"], 0);
2037 }
2038
2039 #[tokio::test]
2040 async fn test_delete_from_array_out_of_bounds() {
2041 let r = exec(json!({
2042 "operation": "delete",
2043 "data": {"items": [1, 2]},
2044 "path": "items.5"
2045 }))
2046 .await;
2047 assert!(!r.is_error);
2048 let v = parse_result(&r);
2050 let items = v["result"]["items"].as_array().unwrap();
2051 assert_eq!(items.len(), 2);
2052 }
2053
2054 #[tokio::test]
2055 async fn test_set_extend_array() {
2056 let r = exec(json!({
2057 "operation": "set",
2058 "data": {"items": [1]},
2059 "path": "items.3",
2060 "value": 99
2061 }))
2062 .await;
2063 assert!(!r.is_error);
2064 let v = parse_result(&r);
2065 let items = v["result"]["items"].as_array().unwrap();
2066 assert_eq!(items.len(), 4);
2067 assert_eq!(items[3], 99);
2068 }
2069
2070 #[tokio::test]
2071 async fn test_filter_with_missing_field_in_some_items() {
2072 let r = exec(json!({
2073 "operation": "filter",
2074 "data": [
2075 {"name": "Alice", "score": 90},
2076 {"name": "Bob"},
2077 {"name": "Carol", "score": 85}
2078 ],
2079 "key": "score",
2080 "operator": "gte",
2081 "value": 85
2082 }))
2083 .await;
2084 assert!(!r.is_error);
2085 let v = parse_result(&r);
2086 assert_eq!(v["result"].as_array().unwrap().len(), 2);
2088 }
2089
2090 #[tokio::test]
2091 async fn test_sort_with_missing_key() {
2092 let r = exec(json!({
2093 "operation": "sort",
2094 "data": [
2095 {"name": "C", "val": 3},
2096 {"name": "A"},
2097 {"name": "B", "val": 1}
2098 ],
2099 "key": "val"
2100 }))
2101 .await;
2102 assert!(!r.is_error);
2103 let v = parse_result(&r);
2105 let arr = v["result"].as_array().unwrap();
2106 assert_eq!(arr.len(), 3);
2107 }
2108}