@@ -387,3 +387,98 @@ def custom_eval(expr, *args, **kwargs):
387387 result = jp .parse (data , eval_func = custom_eval )
388388 assert len (result ) == 2
389389 assert len (eval_calls ) > 0
390+
391+
392+ class TestSliceAdvanced :
393+ """Test advanced slice operations."""
394+
395+ def test_slice_reverse (self ):
396+ data = {"items" : [0 , 1 , 2 , 3 , 4 ]}
397+ assert JSONPath ("$.items[::-1]" ).parse (data ) == [4 , 3 , 2 , 1 , 0 ]
398+
399+ def test_slice_reverse_step (self ):
400+ data = {"items" : [0 , 1 , 2 , 3 , 4 ]}
401+ assert JSONPath ("$.items[::-2]" ).parse (data ) == [4 , 2 , 0 ]
402+
403+ def test_slice_negative_start (self ):
404+ data = {"items" : [0 , 1 , 2 , 3 , 4 ]}
405+ assert JSONPath ("$.items[-2:]" ).parse (data ) == [3 , 4 ]
406+
407+ def test_slice_negative_end (self ):
408+ data = {"items" : [0 , 1 , 2 , 3 , 4 ]}
409+ assert JSONPath ("$.items[:-2]" ).parse (data ) == [0 , 1 , 2 ]
410+
411+
412+ class TestFilterAdvanced :
413+ """Test advanced filter operations."""
414+
415+ def test_filter_not_in_operator (self ):
416+ data = {"items" : [{"tags" : ["a" , "b" ]}, {"tags" : ["c" , "d" ]}]}
417+ assert JSONPath ("$.items[?('a' not in @.tags)].tags" ).parse (data ) == [["c" , "d" ]]
418+
419+ def test_filter_not_operator (self ):
420+ data = {"items" : [{"active" : True }, {"active" : False }]}
421+ assert JSONPath ("$.items[?(not @.active)]" ).parse (data ) == [{"active" : False }]
422+
423+ def test_filter_is_operator (self ):
424+ data = {"items" : [{"v" : None }, {"v" : 1 }]}
425+ assert JSONPath ("$.items[?(@.v is None)]" ).parse (data ) == [{"v" : None }]
426+
427+ def test_filter_is_not_operator (self ):
428+ data = {"items" : [{"v" : None }, {"v" : 1 }]}
429+ assert JSONPath ("$.items[?(@.v is not None)]" ).parse (data ) == [{"v" : 1 }]
430+
431+ def test_filter_nested_attribute (self ):
432+ data = {"items" : [{"x" : {"y" : 1 }}, {"x" : {"y" : 2 }}]}
433+ assert JSONPath ("$.items[?(@.x.y > 1)]" ).parse (data ) == [{"x" : {"y" : 2 }}]
434+
435+ def test_filter_bracket_notation (self ):
436+ data = {"items" : [{"a-b" : 1 }, {"a-b" : 2 }]}
437+ assert JSONPath ("$.items[?(@['a-b'] > 1)]" ).parse (data ) == [{"a-b" : 2 }]
438+
439+
440+ class TestRecursiveDescentAdvanced :
441+ """Test advanced recursive descent operations."""
442+
443+ def test_recursive_with_bracket (self ):
444+ data = {"a" : {"b" : 1 }, "c" : {"b" : 2 }}
445+ assert sorted (JSONPath ("$..['b']" ).parse (data )) == [1 , 2 ]
446+
447+ def test_recursive_with_filter (self ):
448+ data = {"items" : [{"v" : 1 }, {"v" : 2 }]}
449+ result = JSONPath ("$.items[*][?(@.v > 1)]" ).parse (data )
450+ assert result == [{"v" : 2 }]
451+
452+ def test_recursive_multiple_levels (self ):
453+ data = {"a" : {"b" : {"c" : {"d" : 1 }}}}
454+ assert JSONPath ("$..d" ).parse (data ) == [1 ]
455+
456+
457+ class TestJSONPathTypeError :
458+ """Test JSONPathTypeError exception."""
459+
460+ def test_sort_incompatible_types (self ):
461+ from jsonpath import JSONPathTypeError
462+
463+ # Sorting with mixed types that can't be compared
464+ data = {"items" : [{"v" : "a" }, {"v" : 1 }]}
465+ with pytest .raises (JSONPathTypeError , match = "not possible to compare" ):
466+ JSONPath ("$.items[/(v)]" ).parse (data )
467+
468+
469+ class TestInstanceMethod :
470+ """Test JSONPath instance methods."""
471+
472+ def test_search_method (self ):
473+ """JSONPath.search() is an alias for parse()."""
474+ data = {"a" : 1 }
475+ jp = JSONPath ("$.a" )
476+ assert jp .search (data ) == [1 ]
477+ assert jp .search (data , "PATH" ) == ["$.a" ]
478+
479+ def test_parse_reuse (self ):
480+ """Same JSONPath instance can be reused for multiple parse calls."""
481+ jp = JSONPath ("$.a" )
482+ assert jp .parse ({"a" : 1 }) == [1 ]
483+ assert jp .parse ({"a" : 2 }) == [2 ]
484+ assert jp .parse ({"a" : 3 }) == [3 ]
0 commit comments