Skip to main content

argentor_builtins/
json_query.rs

1//! JSON query and manipulation skill.
2//!
3//! Provides a comprehensive set of JSON operations including path-based access,
4//! deep merge, diff, filtering, sorting, flattening, and basic schema validation.
5//!
6//! Inspired by LangChain `JsonGetValueTool`/`JsonListKeysTool` and AutoGPT JSON blocks.
7
8use 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
14/// Skill that exposes JSON manipulation operations to the agent.
15///
16/// Supported operations: `get`, `set`, `delete`, `keys`, `values`, `length`,
17/// `flatten`, `merge`, `diff`, `filter`, `sort`, `pick`, `omit`, `type_of`, `validate`.
18pub struct JsonQuerySkill {
19    descriptor: SkillDescriptor,
20}
21
22impl JsonQuerySkill {
23    /// Create a new `JsonQuerySkill` instance.
24    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
145// ---------------------------------------------------------------------------
146// Helper: require a field from the arguments
147// ---------------------------------------------------------------------------
148
149fn 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
160// ---------------------------------------------------------------------------
161// Path resolution helpers
162// ---------------------------------------------------------------------------
163
164/// Resolve a dot-notation path against a JSON value, returning a reference.
165fn 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
183/// Set a value at a dot-notation path, creating intermediate objects/arrays as needed.
184/// Returns the modified root value.
185fn 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                // If next segment is a number and child is null, create an array
209                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            // Extend with nulls if index is beyond current length
231            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            // Current node is a scalar; need to replace with object or array
250            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
273/// Delete a value at a dot-notation path.
274fn 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
319// ---------------------------------------------------------------------------
320// Operations
321// ---------------------------------------------------------------------------
322
323fn 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            // Keys in b but not in a -> added
472            for (k, v) in b {
473                if !a.contains_key(k) {
474                    added.insert(k.clone(), v.clone());
475                }
476            }
477
478            // Keys in a but not in b -> removed; keys in both but different -> changed
479            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    // Try numeric comparison first
603    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    // Fall back to string comparison
607    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    // Type check
687    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        // JSON Schema allows "integer" as a type
697        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    // Required fields check (only for objects)
708    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    // Properties type check (only for objects)
722    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    // minItems / maxItems check (only for arrays)
753    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// ---------------------------------------------------------------------------
777// Tests
778// ---------------------------------------------------------------------------
779
780#[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    // -----------------------------------------------------------------------
804    // Descriptor
805    // -----------------------------------------------------------------------
806
807    #[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    // -----------------------------------------------------------------------
823    // Missing operation
824    // -----------------------------------------------------------------------
825
826    #[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    // -----------------------------------------------------------------------
841    // get
842    // -----------------------------------------------------------------------
843
844    #[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    // -----------------------------------------------------------------------
918    // set
919    // -----------------------------------------------------------------------
920
921    #[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    // -----------------------------------------------------------------------
992    // delete
993    // -----------------------------------------------------------------------
994
995    #[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    // -----------------------------------------------------------------------
1053    // keys
1054    // -----------------------------------------------------------------------
1055
1056    #[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        // serde_json preserves insertion order
1068        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    // -----------------------------------------------------------------------
1084    // values
1085    // -----------------------------------------------------------------------
1086
1087    #[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    // -----------------------------------------------------------------------
1113    // length
1114    // -----------------------------------------------------------------------
1115
1116    #[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    // -----------------------------------------------------------------------
1163    // flatten
1164    // -----------------------------------------------------------------------
1165
1166    #[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    // -----------------------------------------------------------------------
1216    // merge
1217    // -----------------------------------------------------------------------
1218
1219    #[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); // overwritten
1231        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    // -----------------------------------------------------------------------
1272    // diff
1273    // -----------------------------------------------------------------------
1274
1275    #[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        // a was removed
1286        assert_eq!(v["removed"]["a"], 1);
1287        // d was added
1288        assert_eq!(v["added"]["d"], 4);
1289        // c changed
1290        assert_eq!(v["changed"]["c"]["from"], 3);
1291        assert_eq!(v["changed"]["c"]["to"], 99);
1292        // b unchanged — should not appear anywhere
1293        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    // -----------------------------------------------------------------------
1325    // filter
1326    // -----------------------------------------------------------------------
1327
1328    #[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    // -----------------------------------------------------------------------
1478    // sort
1479    // -----------------------------------------------------------------------
1480
1481    #[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    // -----------------------------------------------------------------------
1560    // pick
1561    // -----------------------------------------------------------------------
1562
1563    #[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    // -----------------------------------------------------------------------
1615    // omit
1616    // -----------------------------------------------------------------------
1617
1618    #[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    // -----------------------------------------------------------------------
1645    // type_of
1646    // -----------------------------------------------------------------------
1647
1648    #[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    // -----------------------------------------------------------------------
1697    // validate
1698    // -----------------------------------------------------------------------
1699
1700    #[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    // -----------------------------------------------------------------------
1828    // Edge cases & integration
1829    // -----------------------------------------------------------------------
1830
1831    #[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        // Array unchanged since index is out of bounds
2049        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        // Bob is excluded because he has no "score" field
2087        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        // Items with missing key get Value::Null, sorted to beginning
2104        let v = parse_result(&r);
2105        let arr = v["result"].as_array().unwrap();
2106        assert_eq!(arr.len(), 3);
2107    }
2108}