@@ -55,6 +55,81 @@ def test_tag_filter_varied(operation, tags, expected):
5555 assert str (tf ) == expected
5656
5757
58+ @pytest .mark .parametrize (
59+ "pattern,expected" ,
60+ [
61+ # Basic prefix wildcard
62+ ("tech*" , "@tag_field:{tech*}" ),
63+ # Multiple patterns via list
64+ (["tech*" , "soft*" ], "@tag_field:{tech*|soft*}" ),
65+ # Wildcard with special chars that still get escaped
66+ ("tech*-pro" , "@tag_field:{tech*\\ -pro}" ),
67+ # Prefix with space (space escaped, wildcard preserved)
68+ ("hello w*" , "@tag_field:{hello\\ w*}" ),
69+ # Multiple wildcards in same pattern
70+ ("*test*" , "@tag_field:{*test*}" ),
71+ # Empty pattern returns wildcard match-all
72+ ("" , "*" ),
73+ ([], "*" ),
74+ (None , "*" ),
75+ # Pattern with special characters
76+ ("cat$*" , "@tag_field:{cat\\ $*}" ),
77+ ],
78+ ids = [
79+ "prefix_wildcard" ,
80+ "multiple_patterns" ,
81+ "wildcard_with_special_char" ,
82+ "prefix_with_space" ,
83+ "multiple_wildcards" ,
84+ "empty_string" ,
85+ "empty_list" ,
86+ "none" ,
87+ "special_char_with_wildcard" ,
88+ ],
89+ )
90+ def test_tag_wildcard_filter (pattern , expected ):
91+ """Test Tag % operator for wildcard/prefix matching."""
92+ tf = Tag ("tag_field" ) % pattern
93+ assert str (tf ) == expected
94+
95+
96+ def test_tag_wildcard_preserves_asterisk ():
97+ """Verify that * is not escaped when using % operator."""
98+ # With == operator, * should be escaped
99+ tf_eq = Tag ("tag_field" ) == "tech*"
100+ assert str (tf_eq ) == "@tag_field:{tech\\ *}"
101+
102+ # With % operator, * should NOT be escaped
103+ tf_like = Tag ("tag_field" ) % "tech*"
104+ assert str (tf_like ) == "@tag_field:{tech*}"
105+
106+
107+ def test_tag_wildcard_combined_with_exact_match ():
108+ """Test combining wildcard and exact match Tag filters in the same query."""
109+ # Create filters with different operators
110+ exact_match = Tag ("brand" ) == "nike"
111+ wildcard_match = Tag ("category" ) % "tech*"
112+
113+ # Verify individual filters work correctly
114+ assert str (exact_match ) == "@brand:{nike}"
115+ assert str (wildcard_match ) == "@category:{tech*}"
116+
117+ # Combine with AND - wildcard should be preserved, exact match should not have *
118+ combined_and = exact_match & wildcard_match
119+ assert str (combined_and ) == "(@brand:{nike} @category:{tech*})"
120+
121+ # Combine with OR
122+ combined_or = exact_match | wildcard_match
123+ assert str (combined_or ) == "(@brand:{nike} | @category:{tech*})"
124+
125+ # More complex: mix of exact, wildcard, and exact with * in value
126+ exact_with_asterisk = Tag ("status" ) == "active*" # * should be escaped
127+ complex_filter = exact_match & wildcard_match & exact_with_asterisk
128+ assert "@brand:{nike}" in str (complex_filter )
129+ assert "@category:{tech*}" in str (complex_filter ) # wildcard preserved
130+ assert "@status:{active\\ *}" in str (complex_filter ) # asterisk escaped
131+
132+
58133def test_nullable ():
59134 tag = Tag ("tag_field" ) == None
60135 assert str (tag ) == "*"
0 commit comments