Refine bounded traversal contracts
This commit is contained in:
parent
c69cd16515
commit
4ae9b31db5
8 changed files with 101 additions and 26 deletions
|
|
@ -24,6 +24,22 @@ class DocForgeCliTests(unittest.TestCase):
|
|||
shutil.copytree(FIXTURES / "alpha", root)
|
||||
return root
|
||||
|
||||
def test_traversal_commands_accept_explicit_result_limits(self) -> None:
|
||||
parser = _parser()
|
||||
for command in ("backlinks", "dependencies", "impact"):
|
||||
with self.subTest(command=command):
|
||||
arguments = parser.parse_args(
|
||||
[
|
||||
"--project-root",
|
||||
"/tmp/project",
|
||||
command,
|
||||
"guide.workflow",
|
||||
"--limit",
|
||||
"7",
|
||||
]
|
||||
)
|
||||
self.assertEqual(7, arguments.limit)
|
||||
|
||||
def test_reindex_apply_and_visualization_commands_are_self_service(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
parent = Path(directory)
|
||||
|
|
|
|||
|
|
@ -353,20 +353,31 @@ class DocForgeCoreTests(unittest.TestCase):
|
|||
self.assertEqual(["guide.workflow"], [item["node_id"] for item in limited["results"]])
|
||||
self.assertEqual(1, limited["limit"])
|
||||
self.assertTrue(limited["truncated"])
|
||||
self.assertEqual("result_limit", limited["truncation_reason"])
|
||||
self.assertLessEqual(
|
||||
limited["examined_edges"],
|
||||
limited["examined_edges_limit"],
|
||||
limited["candidate_edges_consumed"],
|
||||
limited["candidate_edges_limit"],
|
||||
)
|
||||
|
||||
complete_limit = index.dependencies("guide.workflow", depth=2, limit=1)
|
||||
self.assertEqual(1, complete_limit["count"])
|
||||
self.assertFalse(complete_limit["truncated"])
|
||||
self.assertIsNone(complete_limit["truncation_reason"])
|
||||
|
||||
limited_backlinks = index.backlinks("guide.workflow", limit=1)
|
||||
self.assertEqual(1, limited_backlinks["count"])
|
||||
self.assertEqual(1, limited_backlinks["limit"])
|
||||
self.assertFalse(limited_backlinks["truncated"])
|
||||
|
||||
for operation in (
|
||||
lambda: index.backlinks("guide.workflow", limit=0),
|
||||
lambda: index.dependencies("guide.workflow", limit=True),
|
||||
lambda: index.impact("guide.workflow", limit=101),
|
||||
):
|
||||
with self.subTest(operation=operation), self.assertRaises(DocForgeError) as invalid:
|
||||
operation()
|
||||
self.assertEqual("invalid_limit", invalid.exception.code)
|
||||
|
||||
def test_query_rechecks_source_identity_before_returning(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = self.copy_fixture("alpha", Path(directory))
|
||||
|
|
|
|||
|
|
@ -70,6 +70,14 @@ class DocForgeMcpTests(unittest.IsolatedAsyncioTestCase):
|
|||
names = tuple(tool.name for tool in response.tools)
|
||||
self.assertEqual(ALL_TOOLS, names)
|
||||
self.assertEqual(14, len(PROPOSAL_TOOLS))
|
||||
tools = {tool.name: tool for tool in response.tools}
|
||||
for name in (
|
||||
"docforge_backlinks",
|
||||
"docforge_dependencies",
|
||||
"docforge_impact",
|
||||
):
|
||||
self.assertIn("limit", tools[name].inputSchema["properties"])
|
||||
self.assertNotIn("limit", tools[name].inputSchema.get("required", []))
|
||||
self.assertFalse(
|
||||
any(
|
||||
token in name
|
||||
|
|
@ -193,6 +201,24 @@ class DocForgeMcpTests(unittest.IsolatedAsyncioTestCase):
|
|||
self.assertLessEqual(context["estimated_tokens"], 180)
|
||||
self.assertTrue(context["omissions"])
|
||||
|
||||
async def test_invalid_traversal_limit_is_a_structured_domain_error(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = self.copy_fixture("alpha", Path(directory))
|
||||
ProjectIndex(Project.open(root)).build()
|
||||
async with create_connected_server_and_client_session(
|
||||
create_server(root), raise_exceptions=True
|
||||
) as session:
|
||||
result = await session.call_tool(
|
||||
"docforge_impact",
|
||||
{"node_id": "guide.foundation", "limit": 0},
|
||||
)
|
||||
|
||||
self.assertEqual("error", result.structuredContent["status"])
|
||||
self.assertEqual(
|
||||
"invalid_limit",
|
||||
result.structuredContent["error"]["code"],
|
||||
)
|
||||
|
||||
async def test_sync_register_rebase_apply_and_lifecycle_are_one_bound_workflow(
|
||||
self,
|
||||
) -> None:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue