from unittest.mock import AsyncMock, MagicMock import pytest from mcp.server.fastmcp import Context from strava_mcp_server.tools.activities import register class MockMCP: def __init__(self): self.tools = {} def tool(self): def decorator(func): self.tools[func.__name__] = func return func return decorator def resource(self, path, **kwargs): if not hasattr(self, "resources"): self.resources = {} def decorator(func): self.resources[path] = func return func return decorator @pytest.mark.asyncio async def test_get_activity_map_success(): mcp = MockMCP() strava = MagicMock() # Mock detailed activity with a valid polyline strava.get_activity = AsyncMock( return_value={ "name": "Evening Ride", "map": {"polyline": "a~l~FqzbwOq}@it@..."}, "sport_type": "Ride", } ) strava.get_activity_streams = AsyncMock( return_value={ "altitude": {"data": [100.0, 101.0, 102.0]}, "distance": {"data": [0.0, 100.0, 200.0]}, "latlng": {"data": [[47.1, 9.1], [47.2, 9.2], [47.3, 9.3]]}, } ) ctx = AsyncMock(spec=Context) # Register tools register(mcp, strava) # Retrieve and call get_activity_map tool get_activity_map = mcp.tools["get_activity_map"] result = await get_activity_map(ctx, 12345) assert result.isError is not True assert result.meta == {"ui": {"resourceUri": "strava://activity/12345/map"}} content = result.content assert len(content) == 1 assert content[0].type == "text" assert "Die interaktive Karte wurde vorbereitet" in content[0].text # Now fetch and test the actual HTML from the registered resource handler assert "strava://activity/{activity_id}/map" in mcp.resources resource_handler = mcp.resources["strava://activity/{activity_id}/map"] html_text = await resource_handler(12345) assert isinstance(html_text, str) assert "" in html_text assert "unpkg.com/leaflet" in html_text assert "latlngData" in html_text assert "[47.1, 9.1]" in html_text assert 'sportEmoji = "🚴"' in html_text assert "L.control.layers" in html_text assert "satellite" in html_text assert "🟢" in html_text assert "🏁" in html_text @pytest.mark.asyncio async def test_get_activity_map_no_polyline(): mcp = MockMCP() strava = MagicMock() # Mock activity without GPS map info strava.get_activity = AsyncMock( return_value={ "name": "Indoor Trainer", "map": {"polyline": None, "summary_polyline": None}, } ) ctx = AsyncMock(spec=Context) # Register tools register(mcp, strava) # Retrieve and call get_activity_map tool get_activity_map = mcp.tools["get_activity_map"] result = await get_activity_map(ctx, 67890) assert result.isError is True assert len(result.content) == 1 assert result.content[0].type == "text" assert "⚠️ Keine GPS-Routen-Daten" in result.content[0].text