a2d238d92e
Make Zed Agent closer to Cursor: deterministic DevOps path index, live Traefik port probe before blind edits, stop-after-edit, and quieter Russian progress.
155 lines
5.5 KiB
Python
155 lines
5.5 KiB
Python
"""Unit tests for deterministic path index + resolve gating."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import unittest
|
|
|
|
from path_resolve import (
|
|
apply_deterministic_path_index,
|
|
enrich_plan_from_discovery_tools,
|
|
match_index_paths,
|
|
path_resolve_needed,
|
|
)
|
|
|
|
|
|
class TestPathResolve(unittest.TestCase):
|
|
def test_index_hits_traefik_not_fake_yml(self) -> None:
|
|
plan = {
|
|
"user_goal": "fix observer.ift.calentiq.com traefik route",
|
|
"subtasks": [
|
|
{
|
|
"id": "1",
|
|
"prompt": "edit traefik for observer",
|
|
"paths": [
|
|
r"C:\Users\alexc\IdeaProjects\eventHub\EventHubDevOps\ift\traefik\traefik.yml"
|
|
],
|
|
}
|
|
],
|
|
}
|
|
# sanitize would drop fake; index should still hit via keywords
|
|
indexed = match_index_paths(plan)
|
|
self.assertTrue(indexed)
|
|
self.assertTrue(
|
|
indexed[0].lower().replace("/", "\\").endswith(
|
|
r"ift\traefik\dynamic_conf.yml"
|
|
)
|
|
)
|
|
fixed, unresolved = apply_deterministic_path_index(plan)
|
|
self.assertEqual(unresolved, [])
|
|
self.assertFalse(path_resolve_needed(fixed))
|
|
self.assertTrue(
|
|
fixed["subtasks"][0]["paths"][0]
|
|
.lower()
|
|
.replace("/", "\\")
|
|
.endswith(r"ift\traefik\dynamic_conf.yml")
|
|
)
|
|
|
|
def test_index_compose_core(self) -> None:
|
|
plan = {
|
|
"user_goal": "bump timeout in docker compose swarm",
|
|
"subtasks": [{"id": "1", "prompt": "edit compose", "paths": []}],
|
|
}
|
|
fixed, unresolved = apply_deterministic_path_index(plan)
|
|
self.assertEqual(unresolved, [])
|
|
self.assertIn("docker-compose.core.yml", fixed["subtasks"][0]["paths"][0])
|
|
|
|
def test_miss_triggers_queries(self) -> None:
|
|
plan = {
|
|
"user_goal": "EventHubDevOps mystery widget xyz",
|
|
"subtasks": [{"id": "1", "prompt": "widget", "paths": []}],
|
|
}
|
|
fixed, unresolved = apply_deterministic_path_index(plan)
|
|
self.assertTrue(unresolved)
|
|
self.assertTrue(path_resolve_needed(fixed))
|
|
|
|
def test_enrich_from_find_tool(self) -> None:
|
|
plan = {
|
|
"path_resolve_queries": ["dynamic_conf.yml"],
|
|
"path_resolve": "needed",
|
|
"subtasks": [{"id": "1", "prompt": "x", "paths": []}],
|
|
}
|
|
yml = r"C:\Users\alexc\IdeaProjects\eventHub\EventHubDevOps\ift\traefik\dynamic_conf.yml"
|
|
messages = [
|
|
{
|
|
"role": "assistant",
|
|
"tool_calls": [
|
|
{
|
|
"id": "f1",
|
|
"type": "function",
|
|
"function": {
|
|
"name": "find_path",
|
|
"arguments": json.dumps({"query": "dynamic_conf"}),
|
|
},
|
|
}
|
|
],
|
|
},
|
|
{"role": "tool", "tool_call_id": "f1", "content": yml + "\n"},
|
|
]
|
|
enriched = enrich_plan_from_discovery_tools(plan, messages)
|
|
self.assertEqual(enriched["path_resolve"], "find")
|
|
self.assertFalse(path_resolve_needed(enriched))
|
|
self.assertEqual(enriched["subtasks"][0]["paths"][0], yml)
|
|
|
|
|
|
def test_enrich_ignores_yaml_etc_artifacts(self) -> None:
|
|
plan = {
|
|
"path_resolve": "index",
|
|
"subtasks": [
|
|
{
|
|
"id": "1",
|
|
"prompt": "traefik",
|
|
"paths": [
|
|
r"C:\Users\alexc\IdeaProjects\eventHub\EventHubDevOps\ift\traefik\dynamic_conf.yml"
|
|
],
|
|
"path_source": "index",
|
|
}
|
|
],
|
|
}
|
|
# read_file body mentions container paths — must NOT poison plan
|
|
yml_body = (
|
|
"http:\n routers:\n"
|
|
" # volume: /etc/traefik/dynamic_conf.yml\n"
|
|
" # also null:/etc/nginx/conf.d/default.conf\n"
|
|
)
|
|
messages = [
|
|
{
|
|
"role": "assistant",
|
|
"tool_calls": [
|
|
{
|
|
"id": "r1",
|
|
"type": "function",
|
|
"function": {
|
|
"name": "read_file",
|
|
"arguments": json.dumps(
|
|
{
|
|
"path": r"C:\Users\alexc\IdeaProjects\eventHub\EventHubDevOps\ift\traefik\dynamic_conf.yml"
|
|
}
|
|
),
|
|
},
|
|
}
|
|
],
|
|
},
|
|
{"role": "tool", "tool_call_id": "r1", "content": yml_body},
|
|
]
|
|
enriched = enrich_plan_from_discovery_tools(plan, messages)
|
|
self.assertEqual(enriched["path_resolve"], "index")
|
|
self.assertIn(
|
|
"dynamic_conf.yml",
|
|
enriched["subtasks"][0]["paths"][0],
|
|
)
|
|
self.assertNotIn("nginx", json.dumps(enriched).lower())
|
|
# Without index, still ignore read bodies (no find_* call ids)
|
|
plan2 = {
|
|
"path_resolve_queries": ["x"],
|
|
"path_resolve": "needed",
|
|
"subtasks": [{"id": "1", "prompt": "x", "paths": []}],
|
|
}
|
|
enriched2 = enrich_plan_from_discovery_tools(plan2, messages)
|
|
self.assertEqual(enriched2.get("path_resolve"), "needed")
|
|
self.assertEqual(enriched2["subtasks"][0]["paths"], [])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|