This repository has been archived on 2026-03-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Timmy-time-dashboard/tests/test_openfang_tools.py
2026-02-28 19:27:48 -05:00

224 lines
7.1 KiB
Python

"""Chunk 3: OpenFang MCP tool registration — test first, implement second.
Tests cover:
- register_openfang_tools() registers all 7 hands
- Each tool has correct category, tags, and schema
- Twitter hand requires confirmation
- Persona-hand mapping is correct
- Handler delegates to openfang_client.execute_hand()
"""
import asyncio
import json
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
@pytest.fixture(autouse=True)
def clean_tool_registry():
"""Remove OpenFang tools between tests so registration is idempotent."""
yield
from mcp.registry import tool_registry
for name in list(tool_registry._tools.keys()):
if name.startswith("openfang_"):
tool_registry.unregister(name)
# ---------------------------------------------------------------------------
# Registration
# ---------------------------------------------------------------------------
def test_register_openfang_tools_count():
"""register_openfang_tools() should register exactly 7 tools."""
from infrastructure.openfang.tools import register_openfang_tools
count = register_openfang_tools()
assert count == 7
def test_all_seven_hands_registered():
"""After registration, all 7 openfang_* tools exist in the registry."""
from infrastructure.openfang.tools import register_openfang_tools
from mcp.registry import tool_registry
register_openfang_tools()
expected = {
"openfang_browser",
"openfang_collector",
"openfang_predictor",
"openfang_lead",
"openfang_twitter",
"openfang_researcher",
"openfang_clip",
}
registered = set(tool_registry.list_tools(category="openfang"))
assert registered == expected
def test_tools_have_correct_category():
"""Every OpenFang tool should be in the 'openfang' category."""
from infrastructure.openfang.tools import register_openfang_tools
from mcp.registry import tool_registry
register_openfang_tools()
for name in tool_registry.list_tools(category="openfang"):
record = tool_registry.get(name)
assert record is not None
assert record.category == "openfang"
def test_tools_have_vendor_tag():
"""Every OpenFang tool should be tagged with 'vendor'."""
from infrastructure.openfang.tools import register_openfang_tools
from mcp.registry import tool_registry
register_openfang_tools()
for name in tool_registry.list_tools(category="openfang"):
record = tool_registry.get(name)
assert "vendor" in record.tags
assert "openfang" in record.tags
def test_twitter_requires_confirmation():
"""The twitter hand should require user confirmation before execution."""
from infrastructure.openfang.tools import register_openfang_tools
from mcp.registry import tool_registry
register_openfang_tools()
twitter = tool_registry.get("openfang_twitter")
assert twitter is not None
assert twitter.requires_confirmation is True
def test_non_twitter_no_confirmation():
"""Non-twitter hands should NOT require confirmation."""
from infrastructure.openfang.tools import register_openfang_tools
from mcp.registry import tool_registry
register_openfang_tools()
for name in ["openfang_browser", "openfang_collector", "openfang_predictor"]:
record = tool_registry.get(name)
assert record is not None
assert record.requires_confirmation is False
def test_tools_have_schemas():
"""Every OpenFang tool should have a non-empty schema with 'name' and 'parameters'."""
from infrastructure.openfang.tools import register_openfang_tools
from mcp.registry import tool_registry
register_openfang_tools()
for name in tool_registry.list_tools(category="openfang"):
record = tool_registry.get(name)
assert record.schema
assert "name" in record.schema
assert "parameters" in record.schema
# ---------------------------------------------------------------------------
# Persona-hand mapping
# ---------------------------------------------------------------------------
def test_persona_hand_map_mace():
"""Mace (Security) should have collector and browser."""
from infrastructure.openfang.tools import get_hands_for_persona
hands = get_hands_for_persona("mace")
assert "openfang_collector" in hands
assert "openfang_browser" in hands
def test_persona_hand_map_seer():
"""Seer (Analytics) should have predictor and researcher."""
from infrastructure.openfang.tools import get_hands_for_persona
hands = get_hands_for_persona("seer")
assert "openfang_predictor" in hands
assert "openfang_researcher" in hands
def test_persona_hand_map_echo():
"""Echo (Research) should have researcher, browser, and collector."""
from infrastructure.openfang.tools import get_hands_for_persona
hands = get_hands_for_persona("echo")
assert "openfang_researcher" in hands
assert "openfang_browser" in hands
assert "openfang_collector" in hands
def test_persona_hand_map_unknown():
"""Unknown persona should get empty list."""
from infrastructure.openfang.tools import get_hands_for_persona
hands = get_hands_for_persona("nonexistent")
assert hands == []
# ---------------------------------------------------------------------------
# Handler delegation
# ---------------------------------------------------------------------------
@pytest.mark.asyncio
async def test_handler_delegates_to_client():
"""Tool handler should call openfang_client.execute_hand()."""
from infrastructure.openfang.client import HandResult
from infrastructure.openfang.tools import register_openfang_tools
from mcp.registry import tool_registry
register_openfang_tools()
mock_result = HandResult(
hand="browser",
success=True,
output="Page loaded",
)
with patch(
"infrastructure.openfang.tools.openfang_client"
) as mock_client:
mock_client.execute_hand = AsyncMock(return_value=mock_result)
record = tool_registry.get("openfang_browser")
assert record is not None
output = await record.handler(url="https://example.com")
assert output == "Page loaded"
mock_client.execute_hand.assert_called_once_with(
"browser", {"url": "https://example.com"}
)
@pytest.mark.asyncio
async def test_handler_returns_error_on_failure():
"""On failure, handler should return the error string (not raise)."""
from infrastructure.openfang.client import HandResult
from infrastructure.openfang.tools import register_openfang_tools
from mcp.registry import tool_registry
register_openfang_tools()
mock_result = HandResult(
hand="collector",
success=False,
error="Connection refused",
)
with patch(
"infrastructure.openfang.tools.openfang_client"
) as mock_client:
mock_client.execute_hand = AsyncMock(return_value=mock_result)
record = tool_registry.get("openfang_collector")
output = await record.handler(target="example.com")
assert "error" in output.lower()
assert "Connection refused" in output