# #region Test.IntentKeyword.Edges [C:2] [TYPE Module] [SEMANTICS test,metadata,invariants] # @BRIEF Metadata invariant tests for agent tools — tool catalog consistency, docstring coverage. # Keyword matching tests removed (LLM handles intent detection via LangGraph tool-calling). # @RELATION BINDS_TO -> [AgentChat.Tools] # @TEST_EDGE: all_tools_registered -> get_all_tools() returns consistent list # @TEST_EDGE: docstrings_present -> every tool has a non-empty docstring from pathlib import Path import sys sys.path.insert(0, str(Path(__file__).parent.parent.parent / "src")) import pytest from unittest.mock import MagicMock, patch # ═══════════════════════════════════════════════════════════════════ # A — Tool catalog consistency # ═══════════════════════════════════════════════════════════════════ class TestToolCatalog: """Verify tool catalog is internally consistent.""" def test_get_all_tools_returns_all_24(self): """get_all_tools() returns at least 24 tools.""" with patch("ss_tools.agent.tools.logger", MagicMock()): from ss_tools.agent.tools import get_all_tools tools = get_all_tools() tool_names = {t.name for t in tools} assert len(tools) >= 24, f"Expected ≥24 tools, got {len(tools)}: {tool_names}" # Core tools must be present assert "show_capabilities" in tool_names assert "search_dashboards" in tool_names assert "get_health_summary" in tool_names assert "start_maintenance" in tool_names def test_tool_names_are_unique(self): """No duplicate tool names in get_all_tools().""" with patch("ss_tools.agent.tools.logger", MagicMock()): from ss_tools.agent.tools import get_all_tools tools = get_all_tools() names = [t.name for t in tools] assert len(names) == len(set(names)), f"Duplicate tool names: {names}" def test_every_tool_has_docstring(self): """Every tool MUST have a docstring — enforced by LangChain but verified here.""" with patch("ss_tools.agent.tools.logger", MagicMock()): from ss_tools.agent.tools import get_all_tools for t in get_all_tools(): desc = (t.description or "").strip() assert desc, f"Tool '{t.name}' has empty description. LangChain @tool requires a docstring." # ═══════════════════════════════════════════════════════════════════ # B — Embedding router metadata # ═══════════════════════════════════════════════════════════════════ class TestEmbeddingMetadata: """Verify embedding router is consistent with tool catalog.""" def test_embedding_top_k_returns_empty_for_empty_query(self): """Empty query → empty result.""" try: from ss_tools.agent._embedding_router import embedding_top_k except ImportError: pytest.skip("_embedding_router module not importable") assert embedding_top_k("") == [] def test_embedding_is_available_returns_bool(self): """embedding_is_available returns bool, does not raise.""" try: from ss_tools.agent._embedding_router import embedding_is_available except ImportError: pytest.skip("_embedding_router module not importable") result = embedding_is_available() assert isinstance(result, bool) def test_get_descriptions_matches_all_tools(self): """_get_descriptions() covers every tool in get_all_tools() 1:1.""" with patch("ss_tools.agent.tools.logger", MagicMock()): from ss_tools.agent.tools import get_all_tools try: from ss_tools.agent._embedding_router import _get_descriptions except ImportError: pytest.skip("_embedding_router module not importable") descs, names = _get_descriptions() tool_names = {t.name for t in get_all_tools()} assert set(names) == tool_names, ( f"Mismatch: descriptions={set(names) - tool_names}, tools={tool_names - set(names)}" ) assert len(descs) == len(tool_names), f"Expected {len(tool_names)} descriptions, got {len(descs)}" # ═══════════════════════════════════════════════════════════════════ # C — Tool resolver utilities (kept functions) # ═══════════════════════════════════════════════════════════════════ class TestToolResolverUtils: """Utility functions in _tool_resolver still work.""" def test_normalize_tool_args_handles_dict(self): from ss_tools.agent._tool_resolver import normalize_tool_args assert normalize_tool_args({"key": "value"}) == {"key": "value"} def test_normalize_tool_args_handles_none(self): from ss_tools.agent._tool_resolver import normalize_tool_args assert normalize_tool_args(None) == {} def test_coerce_tool_call_from_dict(self): from ss_tools.agent._tool_resolver import coerce_tool_call name, args = coerce_tool_call({"name": "test_tool", "args": {"x": 1}}) assert name == "test_tool" assert args == {"x": 1} def test_extract_tool_call_from_state_empty(self): from ss_tools.agent._tool_resolver import extract_tool_call_from_state from unittest.mock import MagicMock state = MagicMock() state.values = {"messages": []} state.next = None name, args = extract_tool_call_from_state(state) assert name is None assert args == {} def test_known_agent_tool_names(self): from ss_tools.agent._tool_resolver import known_agent_tool_names names = known_agent_tool_names() assert "show_capabilities" in names assert "search_dashboards" in names assert len(names) >= 24 # #endregion Test.IntentKeyword.Edges