"""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()