Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 9638226

Browse filesBrowse files
committed
test(query_list): improve test coverage
why: Several areas of the code needed better test coverage what: - Added tests for keygetter with nested objects and error cases - Added tests for QueryList slicing operations - Added tests for QueryList list behavior and pk_key attribute - Added tests for LOOKUP_NAME_MAP completeness - Added tests for lookup_startswith and lookup_endswith functions - Added tests for SkipDefaultFieldsReprMixin
1 parent 0a6d73e commit 9638226
Copy full SHA for 9638226

File tree

Expand file treeCollapse file tree

1 file changed

+199
-0
lines changed
Filter options
Expand file treeCollapse file tree

1 file changed

+199
-0
lines changed

‎tests/_internal/test_query_list.py

Copy file name to clipboardExpand all lines: tests/_internal/test_query_list.py
+199Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,24 @@
88
import pytest
99

1010
from libtmux._internal.query_list import (
11+
LOOKUP_NAME_MAP,
1112
MultipleObjectsReturned,
1213
ObjectDoesNotExist,
1314
PKRequiredException,
1415
QueryList,
1516
keygetter,
1617
lookup_contains,
18+
lookup_endswith,
1719
lookup_exact,
1820
lookup_icontains,
21+
lookup_iendswith,
1922
lookup_iexact,
2023
lookup_in,
2124
lookup_iregex,
25+
lookup_istartswith,
2226
lookup_nin,
2327
lookup_regex,
28+
lookup_startswith,
2429
parse_lookup,
2530
)
2631

@@ -619,3 +624,197 @@ def test_filter_error_handling() -> None:
619624
empty_args: dict[str, t.Any] = {"": "test"}
620625
result = ql.filter(**empty_args)
621626
assert len(result) == 0
627+
628+
629+
def test_lookup_startswith_endswith_functions() -> None:
630+
"""Test startswith and endswith lookup functions with various types."""
631+
# Test lookup_startswith
632+
assert lookup_startswith("test123", "test") # Basic match
633+
assert not lookup_startswith("test123", "123") # No match at start
634+
assert not lookup_startswith(["test"], "test") # Invalid type for data
635+
assert not lookup_startswith("test", ["test"]) # Invalid type for rhs
636+
assert not lookup_startswith("test", 123) # type: ignore # Invalid type for rhs
637+
638+
# Test lookup_istartswith
639+
assert lookup_istartswith("TEST123", "test") # Case-insensitive match
640+
assert lookup_istartswith("test123", "TEST") # Case-insensitive match reverse
641+
assert not lookup_istartswith("test123", "123") # No match at start
642+
assert not lookup_istartswith(["test"], "test") # Invalid type for data
643+
assert not lookup_istartswith("test", ["test"]) # Invalid type for rhs
644+
assert not lookup_istartswith("test", 123) # type: ignore # Invalid type for rhs
645+
646+
# Test lookup_endswith
647+
assert lookup_endswith("test123", "123") # Basic match
648+
assert not lookup_endswith("test123", "test") # No match at end
649+
assert not lookup_endswith(["test"], "test") # Invalid type for data
650+
assert not lookup_endswith("test", ["test"]) # Invalid type for rhs
651+
assert not lookup_endswith("test", 123) # type: ignore # Invalid type for rhs
652+
653+
# Test lookup_iendswith
654+
assert lookup_iendswith("test123", "123") # Basic match
655+
assert lookup_iendswith("test123", "123") # Case-insensitive match
656+
assert lookup_iendswith("test123", "123") # Case-insensitive match reverse
657+
assert not lookup_iendswith("test123", "test") # No match at end
658+
assert not lookup_iendswith(["test"], "test") # Invalid type for data
659+
assert not lookup_iendswith("test", ["test"]) # Invalid type for rhs
660+
assert not lookup_iendswith("test", 123) # type: ignore # Invalid type for rhs
661+
662+
663+
def test_query_list_eq_numeric_comparison() -> None:
664+
"""Test QueryList __eq__ method with numeric comparisons."""
665+
# Test exact numeric matches
666+
ql1 = QueryList([{"a": 1, "b": 2.0}])
667+
ql2 = QueryList([{"a": 1, "b": 2.0}])
668+
assert ql1 == ql2
669+
670+
# Test numeric comparison within tolerance (difference < 1)
671+
ql3 = QueryList([{"a": 1.1, "b": 2.1}])
672+
assert ql1 == ql3 # Should be equal since difference is less than 1
673+
674+
# Test numeric comparison outside tolerance (difference > 1)
675+
ql4 = QueryList([{"a": 2.5, "b": 3.5}])
676+
assert ql1 != ql4 # Should not be equal since difference is more than 1
677+
678+
# Test mixed numeric types
679+
ql5 = QueryList([{"a": 1, "b": 2}]) # int instead of float
680+
assert ql1 == ql5 # Should be equal since values are equivalent
681+
682+
# Test with nested numeric values
683+
ql6 = QueryList([{"a": {"x": 1.0, "y": 2.0}}])
684+
ql7 = QueryList([{"a": {"x": 1.1, "y": 2.1}}])
685+
assert ql6 == ql7 # Should be equal since differences are less than 1
686+
687+
# Test with mixed content
688+
ql10 = QueryList([{"a": 1, "b": "test"}])
689+
ql11 = QueryList([{"a": 1.1, "b": "test"}])
690+
assert ql10 == ql11 # Should be equal since numeric difference is less than 1
691+
692+
# Test with non-dict content (exact equality required)
693+
ql8 = QueryList([1, 2, 3])
694+
ql9 = QueryList([1, 2, 3])
695+
assert ql8 == ql9 # Should be equal since values are exactly the same
696+
assert ql8 != QueryList(
697+
[1.1, 2.1, 3.1]
698+
) # Should not be equal since values are different
699+
700+
701+
def test_keygetter_nested_objects() -> None:
702+
"""Test keygetter function with nested objects."""
703+
704+
@dataclasses.dataclass
705+
class Food:
706+
fruit: list[str] = dataclasses.field(default_factory=list)
707+
breakfast: str | None = None
708+
709+
@dataclasses.dataclass
710+
class Restaurant:
711+
place: str
712+
city: str
713+
state: str
714+
food: Food = dataclasses.field(default_factory=Food)
715+
716+
# Test with nested dataclass
717+
restaurant = Restaurant(
718+
place="Largo",
719+
city="Tampa",
720+
state="Florida",
721+
food=Food(fruit=["banana", "orange"], breakfast="cereal"),
722+
)
723+
assert keygetter(restaurant, "food") == Food(
724+
fruit=["banana", "orange"], breakfast="cereal"
725+
)
726+
assert keygetter(restaurant, "food__breakfast") == "cereal"
727+
assert keygetter(restaurant, "food__fruit") == ["banana", "orange"]
728+
729+
# Test with non-existent attribute (returns None due to exception handling)
730+
with suppress(Exception):
731+
assert keygetter(restaurant, "nonexistent") is None
732+
733+
# Test with invalid path format (returns the object itself)
734+
assert keygetter(restaurant, "") == restaurant
735+
assert keygetter(restaurant, "__") == restaurant
736+
737+
# Test with non-mapping object (returns the object itself)
738+
non_mapping = "not a mapping"
739+
assert keygetter(non_mapping, "any_key") == non_mapping # type: ignore
740+
741+
742+
def test_query_list_slicing() -> None:
743+
"""Test QueryList slicing operations."""
744+
ql = QueryList([1, 2, 3, 4, 5])
745+
746+
# Test positive indices
747+
assert ql[1:3] == QueryList([2, 3])
748+
assert ql[0:5:2] == QueryList([1, 3, 5])
749+
750+
# Test negative indices
751+
assert ql[-3:] == QueryList([3, 4, 5])
752+
assert ql[:-2] == QueryList([1, 2, 3])
753+
assert ql[-4:-2] == QueryList([2, 3])
754+
755+
# Test steps
756+
assert ql[::2] == QueryList([1, 3, 5])
757+
assert ql[::-1] == QueryList([5, 4, 3, 2, 1])
758+
assert ql[4:0:-2] == QueryList([5, 3])
759+
760+
# Test empty slices
761+
assert ql[5:] == QueryList([])
762+
assert ql[-1:-5] == QueryList([])
763+
764+
765+
def test_query_list_attributes() -> None:
766+
"""Test QueryList list behavior and pk_key attribute."""
767+
# Test list behavior
768+
ql = QueryList([1, 2, 3])
769+
assert list(ql) == [1, 2, 3]
770+
assert len(ql) == 3
771+
assert ql[0] == 1
772+
assert ql[-1] == 3
773+
774+
# Test pk_key attribute with objects
775+
@dataclasses.dataclass
776+
class Item:
777+
id: str
778+
value: int
779+
780+
items = [Item("1", 1), Item("2", 2)]
781+
ql = QueryList(items)
782+
ql.pk_key = "id"
783+
assert ql.items() == [("1", items[0]), ("2", items[1])]
784+
785+
# Test pk_key with non-existent attribute
786+
ql.pk_key = "nonexistent"
787+
with pytest.raises(AttributeError):
788+
ql.items()
789+
790+
# Test pk_key with None
791+
ql.pk_key = None
792+
with pytest.raises(PKRequiredException):
793+
ql.items()
794+
795+
796+
def test_lookup_name_map() -> None:
797+
"""Test LOOKUP_NAME_MAP contains all lookup functions."""
798+
# Test all lookup functions are in the map
799+
assert LOOKUP_NAME_MAP["eq"] == lookup_exact
800+
assert LOOKUP_NAME_MAP["exact"] == lookup_exact
801+
assert LOOKUP_NAME_MAP["iexact"] == lookup_iexact
802+
assert LOOKUP_NAME_MAP["contains"] == lookup_contains
803+
assert LOOKUP_NAME_MAP["icontains"] == lookup_icontains
804+
assert LOOKUP_NAME_MAP["startswith"] == lookup_startswith
805+
assert LOOKUP_NAME_MAP["istartswith"] == lookup_istartswith
806+
assert LOOKUP_NAME_MAP["endswith"] == lookup_endswith
807+
assert LOOKUP_NAME_MAP["iendswith"] == lookup_iendswith
808+
assert LOOKUP_NAME_MAP["in"] == lookup_in
809+
assert LOOKUP_NAME_MAP["nin"] == lookup_nin
810+
assert LOOKUP_NAME_MAP["regex"] == lookup_regex
811+
assert LOOKUP_NAME_MAP["iregex"] == lookup_iregex
812+
813+
# Test lookup functions behavior through the map
814+
data = "test123"
815+
assert LOOKUP_NAME_MAP["contains"](data, "test")
816+
assert LOOKUP_NAME_MAP["icontains"](data, "TEST")
817+
assert LOOKUP_NAME_MAP["startswith"](data, "test")
818+
assert LOOKUP_NAME_MAP["endswith"](data, "123")
819+
assert not LOOKUP_NAME_MAP["in"](data, ["other", "values"])
820+
assert LOOKUP_NAME_MAP["regex"](data, r"\d+")

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.