Skip to content

Commit 4ddef38

Browse files
committed
test(evaluators): expand starts_with coverage
1 parent 5d29b8a commit 4ddef38

1 file changed

Lines changed: 133 additions & 0 deletions

File tree

‎evaluators/builtin/tests/list/test_list.py‎

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,27 @@ async def test_starts_with_matches_prefix(self) -> None:
5454
assert result.matched is True
5555
assert result.metadata["matches"] == ["/home/lev/agent-control/server/src/app.py"]
5656

57+
@pytest.mark.asyncio
58+
async def test_starts_with_matches_exact_path_value(self) -> None:
59+
"""Test that starts_with mode matches the configured path value exactly."""
60+
# Given: a starts_with evaluator config
61+
evaluator = ListEvaluator(
62+
ListEvaluatorConfig(
63+
values=["/home/lev/agent-control"],
64+
logic="any",
65+
match_on="match",
66+
match_mode="starts_with",
67+
case_sensitive=True,
68+
)
69+
)
70+
71+
# When: evaluating the exact configured path
72+
result = await evaluator.evaluate("/home/lev/agent-control")
73+
74+
# Then: the exact path matches
75+
assert result.matched is True
76+
assert result.metadata["matches"] == ["/home/lev/agent-control"]
77+
5778
@pytest.mark.asyncio
5879
async def test_starts_with_no_match_when_prefix_absent(self) -> None:
5980
"""Test that starts_with mode does not trigger when no prefix matches."""
@@ -74,6 +95,27 @@ async def test_starts_with_no_match_when_prefix_absent(self) -> None:
7495
# Then: the evaluator does not trigger
7596
assert result.matched is False
7697

98+
@pytest.mark.asyncio
99+
async def test_starts_with_uses_raw_string_prefix_for_path_like_values(self) -> None:
100+
"""Test that starts_with is generic string-prefix matching, not path-segment aware."""
101+
# Given: a starts_with evaluator configured with a path-like prefix
102+
evaluator = ListEvaluator(
103+
ListEvaluatorConfig(
104+
values=["/home/lev/agent-control"],
105+
logic="any",
106+
match_on="match",
107+
match_mode="starts_with",
108+
case_sensitive=True,
109+
)
110+
)
111+
112+
# When: evaluating a sibling path that shares the same string prefix
113+
result = await evaluator.evaluate("/home/lev/agent-control-old/server")
114+
115+
# Then: the evaluator matches because starts_with is not path-boundary aware
116+
assert result.matched is True
117+
assert result.metadata["matches"] == ["/home/lev/agent-control-old/server"]
118+
77119
@pytest.mark.asyncio
78120
async def test_starts_with_honors_case_sensitivity(self) -> None:
79121
"""Test that starts_with mode respects case sensitivity settings."""
@@ -127,6 +169,97 @@ async def test_starts_with_supports_no_match_allowlists(self) -> None:
127169
assert allowed_result.matched is False
128170
assert denied_result.matched is True
129171

172+
@pytest.mark.asyncio
173+
async def test_starts_with_matches_plain_text_prefix(self) -> None:
174+
"""Test that starts_with mode works for ordinary non-path strings."""
175+
# Given: a starts_with evaluator config for ordinary strings
176+
evaluator = ListEvaluator(
177+
ListEvaluatorConfig(
178+
values=["agent", "control:"],
179+
logic="any",
180+
match_on="match",
181+
match_mode="starts_with",
182+
case_sensitive=True,
183+
)
184+
)
185+
186+
# When: evaluating values with and without the configured prefixes
187+
matched_result = await evaluator.evaluate("agent-control")
188+
unmatched_result = await evaluator.evaluate("please control: now")
189+
190+
# Then: only the true prefix match triggers
191+
assert matched_result.matched is True
192+
assert unmatched_result.matched is False
193+
194+
@pytest.mark.asyncio
195+
async def test_starts_with_escapes_regex_metacharacters(self) -> None:
196+
"""Test that starts_with treats configured values as literals, not regex."""
197+
# Given: prefixes containing regex metacharacters
198+
evaluator = ListEvaluator(
199+
ListEvaluatorConfig(
200+
values=["release/v1.2+", "[beta]"],
201+
logic="any",
202+
match_on="match",
203+
match_mode="starts_with",
204+
case_sensitive=True,
205+
)
206+
)
207+
208+
# When: evaluating values that begin with those literal prefixes
209+
release_result = await evaluator.evaluate("release/v1.2+rc1")
210+
beta_result = await evaluator.evaluate("[beta] feature flag")
211+
212+
# Then: both values match literally
213+
assert release_result.matched is True
214+
assert beta_result.matched is True
215+
216+
@pytest.mark.asyncio
217+
async def test_starts_with_supports_list_input_with_any_logic(self) -> None:
218+
"""Test that starts_with works on list inputs when any item matches."""
219+
# Given: a starts_with evaluator with any-item semantics
220+
evaluator = ListEvaluator(
221+
ListEvaluatorConfig(
222+
values=["/home/lev/agent-control", "agent"],
223+
logic="any",
224+
match_on="match",
225+
match_mode="starts_with",
226+
case_sensitive=True,
227+
)
228+
)
229+
230+
# When: evaluating a list where only one element matches
231+
result = await evaluator.evaluate(["/var/log/system.log", "agent-control"])
232+
233+
# Then: the evaluator triggers and reports the matching entry
234+
assert result.matched is True
235+
assert result.metadata["matches"] == ["agent-control"]
236+
237+
@pytest.mark.asyncio
238+
async def test_starts_with_supports_list_input_with_all_logic(self) -> None:
239+
"""Test that starts_with respects all-item semantics for list inputs."""
240+
# Given: a starts_with evaluator with all-item semantics
241+
evaluator = ListEvaluator(
242+
ListEvaluatorConfig(
243+
values=["/home/lev/agent-control", "/tmp/cache"],
244+
logic="all",
245+
match_on="match",
246+
match_mode="starts_with",
247+
case_sensitive=True,
248+
)
249+
)
250+
251+
# When: evaluating one fully matching list and one partially matching list
252+
matching_result = await evaluator.evaluate(
253+
["/home/lev/agent-control/server", "/tmp/cache/build"]
254+
)
255+
partial_result = await evaluator.evaluate(
256+
["/home/lev/agent-control/server", "/var/log/system.log"]
257+
)
258+
259+
# Then: only the fully matching list triggers
260+
assert matching_result.matched is True
261+
assert partial_result.matched is False
262+
130263
@pytest.mark.asyncio
131264
async def test_legacy_empty_string_value_is_ignored_defensively(self) -> None:
132265
"""Test that legacy invalid configs do not compile into a match-all regex."""

0 commit comments

Comments
 (0)