From b4269ecd88b055a0c21c864ad38610b2d351e13c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 5 Apr 2026 15:56:57 +0000 Subject: [PATCH] Add comprehensive tests for ToolPool and assemble_tool_pool Tests cover: - assemble_tool_pool returns ToolPool with correct types - Default mode produces non-empty tools - simple_mode and include_mcp flags stored correctly - simple_mode reduces tool set to BashTool/FileReadTool/FileEditTool - include_mcp=False excludes MCP tools - Permission context filters blocked tools - as_markdown includes header, flags, and tool count - as_markdown shows at most 15 tools - Empty tools tuple renders correctly - Tool lines contain name and source_hint - Frozen dataclass prevents mutation Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: abdoelsayed2016 <27821589+abdoelsayed2016@users.noreply.github.com> --- tests/test_tool_pool.py | 106 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 tests/test_tool_pool.py diff --git a/tests/test_tool_pool.py b/tests/test_tool_pool.py new file mode 100644 index 0000000..d2386b9 --- /dev/null +++ b/tests/test_tool_pool.py @@ -0,0 +1,106 @@ +from __future__ import annotations + +import unittest + +from src.models import PortingModule +from src.permissions import ToolPermissionContext +from src.tool_pool import ToolPool, assemble_tool_pool + + +class TestAssembleToolPool(unittest.TestCase): + def test_returns_tool_pool_with_tools(self) -> None: + pool = assemble_tool_pool() + self.assertIsInstance(pool, ToolPool) + self.assertIsInstance(pool.tools, tuple) + self.assertTrue(all(isinstance(t, PortingModule) for t in pool.tools)) + + def test_default_mode_includes_tools(self) -> None: + pool = assemble_tool_pool() + self.assertGreater(len(pool.tools), 0) + + def test_simple_mode_flag_stored(self) -> None: + pool_default = assemble_tool_pool() + pool_simple = assemble_tool_pool(simple_mode=True) + self.assertFalse(pool_default.simple_mode) + self.assertTrue(pool_simple.simple_mode) + + def test_include_mcp_flag_stored(self) -> None: + pool_default = assemble_tool_pool() + pool_no_mcp = assemble_tool_pool(include_mcp=False) + self.assertTrue(pool_default.include_mcp) + self.assertFalse(pool_no_mcp.include_mcp) + + def test_simple_mode_reduces_tools(self) -> None: + pool_full = assemble_tool_pool(simple_mode=False) + pool_simple = assemble_tool_pool(simple_mode=True) + self.assertGreater(len(pool_full.tools), len(pool_simple.tools)) + simple_names = {t.name for t in pool_simple.tools} + self.assertTrue(simple_names.issubset({'BashTool', 'FileReadTool', 'FileEditTool'})) + + def test_include_mcp_false_excludes_mcp_tools(self) -> None: + pool = assemble_tool_pool(include_mcp=False) + for tool in pool.tools: + self.assertNotIn('mcp', tool.name.lower()) + self.assertNotIn('mcp', tool.source_hint.lower()) + + def test_permission_context_filters_blocked_tools(self) -> None: + ctx = ToolPermissionContext.from_iterables(deny_names=['BashTool']) + pool = assemble_tool_pool(permission_context=ctx) + tool_names = {t.name for t in pool.tools} + self.assertNotIn('BashTool', tool_names) + + pool_unfiltered = assemble_tool_pool() + unfiltered_names = {t.name for t in pool_unfiltered.tools} + self.assertIn('BashTool', unfiltered_names) + + +class TestToolPoolAsMarkdown(unittest.TestCase): + def test_includes_header_and_tool_count(self) -> None: + pool = assemble_tool_pool() + md = pool.as_markdown() + self.assertIn('# Tool Pool', md) + self.assertIn(f'Tool count: {len(pool.tools)}', md) + + def test_includes_mode_flags(self) -> None: + pool = assemble_tool_pool(simple_mode=True, include_mcp=False) + md = pool.as_markdown() + self.assertIn('Simple mode: True', md) + self.assertIn('Include MCP: False', md) + + def test_shows_at_most_15_tools(self) -> None: + pool = assemble_tool_pool() + self.assertGreater(len(pool.tools), 15, 'Need >15 tools for this test') + md = pool.as_markdown() + tool_lines = [line for line in md.splitlines() if line.startswith('- ')] + self.assertEqual(len(tool_lines), 15) + + def test_empty_tools_renders_correctly(self) -> None: + pool = ToolPool(tools=(), simple_mode=False, include_mcp=True) + md = pool.as_markdown() + self.assertIn('# Tool Pool', md) + self.assertIn('Tool count: 0', md) + self.assertIn('Simple mode: False', md) + self.assertIn('Include MCP: True', md) + tool_lines = [line for line in md.splitlines() if line.startswith('- ')] + self.assertEqual(len(tool_lines), 0) + + def test_tool_lines_contain_name_and_source_hint(self) -> None: + tool = PortingModule( + name='TestTool', + responsibility='testing', + source_hint='test/path.ts', + ) + pool = ToolPool(tools=(tool,), simple_mode=False, include_mcp=False) + md = pool.as_markdown() + self.assertIn('- TestTool — test/path.ts', md) + + +class TestToolPoolFrozen(unittest.TestCase): + def test_cannot_mutate_fields(self) -> None: + pool = ToolPool(tools=(), simple_mode=False, include_mcp=True) + with self.assertRaises(AttributeError): + pool.simple_mode = True # type: ignore[misc] + + +if __name__ == '__main__': + unittest.main()