restrcuted repo

This commit is contained in:
2025-09-26 06:52:27 -07:00
parent 997028f0e1
commit 15974bbac5
26 changed files with 17 additions and 12 deletions

0
tests/__init__.py Normal file
View File

195
tests/minimal_mcp_test.py Normal file
View File

@@ -0,0 +1,195 @@
#!/usr/bin/env python3
"""
Minimal MCP Test - Just test MCP connection and user profile
"""
import asyncio
import json
import logging
import os
import yaml
from pathlib import Path
# Minimal imports - just what we need
try:
from pydantic_ai.mcp import MCPServerStdio
import shutil
MCP_AVAILABLE = True
except ImportError:
print("❌ pydantic-ai MCP not available")
print("Install with: pip install pydantic-ai")
exit(1)
# Simple logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class MinimalMCPTest:
"""Minimal MCP test class"""
def __init__(self, garth_token: str, server_path: str = "uvx"):
self.garth_token = garth_token
self.server_path = server_path
self.mcp_server = None
self.cached_profile = None
def setup_mcp_server(self):
"""Setup MCP server connection"""
# Set environment
os.environ["GARTH_TOKEN"] = self.garth_token
env = os.environ.copy()
# Find server executable
server_executable = shutil.which(self.server_path)
if not server_executable:
raise FileNotFoundError(f"'{self.server_path}' not found in PATH")
self.mcp_server = MCPServerStdio(
command=server_executable,
args=["garth-mcp-server"],
env=env,
)
print("✅ MCP server configured")
async def test_connection(self):
"""Test basic MCP connection"""
if not self.mcp_server:
raise RuntimeError("MCP server not configured")
try:
# Try to list tools
tools = await self.mcp_server.list_tools()
print(f"✅ MCP connected - found {len(tools)} tools")
# Show tools
for tool in tools:
print(f" 📋 {tool.name}: {getattr(tool, 'description', 'No description')}")
return tools
except Exception as e:
print(f"❌ MCP connection failed: {e}")
raise
async def get_user_profile(self):
"""Get and cache user profile"""
try:
print("📞 Calling user_profile tool...")
# Direct tool call
result = await self.mcp_server.direct_call_tool("user_profile", {})
profile_data = result.output if hasattr(result, 'output') else result
# Cache it
self.cached_profile = profile_data
print("✅ User profile retrieved and cached")
return profile_data
except Exception as e:
print(f"❌ Failed to get user profile: {e}")
raise
def print_profile(self):
"""Print cached profile"""
if not self.cached_profile:
print("❌ No cached profile")
return
print("\n" + "="*50)
print("USER PROFILE")
print("="*50)
print(json.dumps(self.cached_profile, indent=2, default=str))
print("="*50)
async def run_test(self):
"""Run the complete test"""
print("🚀 Starting Minimal MCP Test\n")
# Setup
self.setup_mcp_server()
# Test connection
tools = await self.test_connection()
# Check if user_profile tool exists
user_profile_tool = next((t for t in tools if t.name == "user_profile"), None)
if not user_profile_tool:
print("❌ user_profile tool not found")
return False
# Get user profile
await self.get_user_profile()
# Show results
self.print_profile()
print("\n🎉 Test completed successfully!")
return True
def get_config():
"""Get configuration from config.yaml or environment"""
config_file = Path("config.yaml")
# Try to load from config.yaml first
if config_file.exists():
try:
with open(config_file, 'r') as f:
config_data = yaml.safe_load(f)
garth_token = config_data.get("garth_token")
server_path = config_data.get("garth_mcp_server_path", "uvx")
if garth_token and garth_token != "your_garth_token_here":
print("✅ Configuration loaded from config.yaml")
return garth_token, server_path
else:
print("⚠️ garth_token not properly set in config.yaml")
except yaml.YAMLError as e:
print(f"❌ Error parsing config.yaml: {e}")
except Exception as e:
print(f"❌ Error reading config.yaml: {e}")
else:
print(" config.yaml not found")
# Fallback to environment variables
print("Trying environment variables...")
garth_token = os.getenv("GARTH_TOKEN")
if not garth_token:
print("❌ GARTH_TOKEN not found in config.yaml or environment")
print("Please either:")
print("1. Create config.yaml with garth_token")
print("2. Run 'uvx garth login' and set GARTH_TOKEN environment variable")
raise ValueError("GARTH_TOKEN is required")
server_path = os.getenv("GARTH_MCP_SERVER_PATH", "uvx")
print("✅ Configuration loaded from environment variables")
return garth_token, server_path
async def main():
"""Main entry point"""
try:
# Get config
garth_token, server_path = get_config()
# Run test
test = MinimalMCPTest(garth_token, server_path)
success = await test.run_test()
if success:
print("\n✅ All tests passed!")
else:
print("\n❌ Tests failed!")
except KeyboardInterrupt:
print("\n👋 Interrupted by user")
except Exception as e:
print(f"\n💥 Error: {e}")
logger.error("Test error", exc_info=True)
if __name__ == "__main__":
asyncio.run(main())

View File

@@ -0,0 +1,63 @@
#!/usr/bin/env python3
"""
Simple Garth Test - Minimal test to verify Garth is working
"""
import garth
from pathlib import Path
def simple_test():
print("🔧 Simple Garth Connection Test")
print("=" * 40)
# Check if session exists
session_path = Path.home() / ".garth"
if session_path.exists():
print("✅ Session file exists")
try:
garth.resume(str(session_path))
print("✅ Session loaded")
except Exception as e:
print(f"❌ Session load failed: {e}")
return False
else:
print("❌ No session file found")
return False
# Try the simplest possible API call
simple_endpoints = [
"/",
"/ping",
"/health",
"/status"
]
print("\n🧪 Testing simple endpoints...")
for endpoint in simple_endpoints:
try:
result = garth.connectapi(endpoint)
print(f"{endpoint} worked: {type(result)}")
return True
except Exception as e:
print(f"{endpoint} failed: {str(e)[:50]}")
# Try to access the raw client
print("\n🔍 Checking Garth client details...")
try:
client = garth.client
print(f"Client type: {type(client)}")
print(f"Client attributes: {[attr for attr in dir(client) if not attr.startswith('_')][:5]}")
# Try to see if we can get base URL
if hasattr(client, 'domain'):
print(f"Domain: {client.domain}")
if hasattr(client, 'base_url'):
print(f"Base URL: {client.base_url}")
except Exception as e:
print(f"❌ Client inspection failed: {e}")
return False
if __name__ == "__main__":
simple_test()

204
tests/standalone_test.py Normal file
View File

@@ -0,0 +1,204 @@
#!/usr/bin/env python3
"""
Standalone MCP Test - Single file test for MCP connection and user profile
No external dependencies on the modular architecture - just tests MCP directly
"""
import asyncio
import json
import os
import shutil
import yaml
from pathlib import Path
# Check dependencies
try:
from pydantic_ai.mcp import MCPServerStdio
print("✅ pydantic-ai MCP available")
except ImportError:
print("❌ pydantic-ai MCP not available")
print("Install with: pip install pydantic-ai")
exit(1)
def load_config_from_yaml():
"""Load configuration from config.yaml file"""
config_file = Path("config.yaml")
if not config_file.exists():
print("❌ config.yaml not found")
print("Please create config.yaml with your settings:")
print("""
garth_token: "your_garth_token_here"
openrouter_api_key: "your_openrouter_api_key_here"
openrouter_model: "deepseek/deepseek-chat-v3.1"
garth_mcp_server_path: "uvx"
""")
return None
try:
with open(config_file, 'r') as f:
config_data = yaml.safe_load(f)
print(f"✅ Loaded config from {config_file}")
return config_data
except yaml.YAMLError as e:
print(f"❌ Error parsing config.yaml: {e}")
return None
except Exception as e:
print(f"❌ Error reading config.yaml: {e}")
return None
async def test_mcp_user_profile():
"""Simple test to connect to MCP and get user profile"""
print("🚀 MCP User Profile Test")
print("=" * 40)
# 1. Load configuration from config.yaml
config = load_config_from_yaml()
if not config:
return False
# 2. Get garth_token from config
garth_token = config.get("garth_token")
if not garth_token or garth_token == "your_garth_token_here":
print("❌ garth_token not properly set in config.yaml")
print("Please run: uvx garth login")
print("Then update config.yaml with your token")
return False
print("✅ GARTH_TOKEN loaded from config.yaml")
# 3. Get server path from config
server_path = config.get("garth_mcp_server_path", "uvx")
server_executable = shutil.which(server_path)
if not server_executable:
print(f"{server_path} not found")
print("Please install uvx and garth-mcp-server")
return False
print(f"{server_path} found")
# 4. Setup MCP server
print("🔧 Setting up MCP server...")
env = os.environ.copy()
env["GARTH_TOKEN"] = garth_token
mcp_server = MCPServerStdio(
command=server_executable,
args=["garth-mcp-server"],
env=env,
)
try:
# 5. List available tools
print("📋 Listing MCP tools...")
tools = await mcp_server.list_tools()
print(f"Found {len(tools)} tools:")
for tool in tools:
print(f"{tool.name}")
# 6. Check for user_profile tool
user_profile_tool = next((t for t in tools if t.name == "user_profile"), None)
if not user_profile_tool:
print("❌ user_profile tool not available")
return False
print("✅ user_profile tool found")
# 7. Call user_profile tool
print("📞 Getting user profile...")
result = await mcp_server.direct_call_tool("user_profile", {})
# Extract data
profile_data = result.output if hasattr(result, 'output') else result
# 8. Display results
print("\n" + "=" * 50)
print("USER PROFILE RETRIEVED")
print("=" * 50)
print(json.dumps(profile_data, indent=2, default=str))
print("=" * 50)
# 9. Quick analysis
if isinstance(profile_data, dict):
print(f"\n📊 Profile contains {len(profile_data)} fields:")
for key in list(profile_data.keys())[:5]: # Show first 5 keys
print(f"{key}")
if len(profile_data) > 5:
print(f" ... and {len(profile_data) - 5} more")
print("\n🎉 Test completed successfully!")
# 10. Show config info used
print(f"\n📝 Configuration used:")
print(f" • Model: {config.get('openrouter_model', 'Not set')}")
print(f" • OpenRouter API Key: {'Set' if config.get('openrouter_api_key') else 'Not set'}")
print(f" • Server Path: {server_path}")
return True
except Exception as e:
print(f"❌ Error during test: {e}")
print(f"Error type: {type(e).__name__}")
return False
async def main():
"""Run the test"""
try:
success = await test_mcp_user_profile()
if success:
print("\n✅ MCP user profile test PASSED")
else:
print("\n❌ MCP user profile test FAILED")
except KeyboardInterrupt:
print("\n👋 Test interrupted")
except Exception as e:
print(f"\n💥 Unexpected error: {e}")
if __name__ == "__main__":
print("Standalone MCP User Profile Test")
print("This will test MCP connection and retrieve your Garmin user profile")
print()
# Check prerequisites
print("Prerequisites check:")
# Check if config.yaml exists
config_file = Path("config.yaml")
if config_file.exists():
print("✅ config.yaml found")
try:
with open(config_file, 'r') as f:
config = yaml.safe_load(f)
# Check garth_token in config
if config.get("garth_token") and config.get("garth_token") != "your_garth_token_here":
print("✅ garth_token set in config.yaml")
else:
print("❌ garth_token not properly set in config.yaml")
# Check openrouter_api_key
if config.get("openrouter_api_key") and config.get("openrouter_api_key") != "your_openrouter_api_key_here":
print("✅ openrouter_api_key set in config.yaml")
else:
print("❌ openrouter_api_key not set in config.yaml")
except Exception as e:
print(f"❌ Error reading config.yaml: {e}")
else:
print("❌ config.yaml not found")
if shutil.which("uvx"):
print("✅ uvx command available")
else:
print("❌ uvx not found - install it first")
print()
# Run the test
asyncio.run(main())

445
tests/test_custom_mcp.py Normal file
View File

@@ -0,0 +1,445 @@
#!/usr/bin/env python3
"""
Test Custom MCP Implementation
Test our custom Garth MCP wrapper
"""
import asyncio
import json
import logging
import sys
from pathlib import Path
# Import our modules
from config import Config, load_config, create_sample_config
from mcp_client import MCPClient # Updated MCP client
from cache_manager import CacheManager
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
class CustomMCPTestApp:
"""Test application for custom MCP implementation"""
def __init__(self, config: Config):
self.config = config
self.mcp_client = MCPClient(config) # Will use custom implementation
async def initialize(self):
"""Initialize MCP client"""
logger.info("Initializing Custom MCP test app...")
await self.mcp_client.initialize()
async def cleanup(self):
"""Cleanup resources"""
await self.mcp_client.cleanup()
async def test_connection_and_tools(self):
"""Test basic connection and list tools"""
print("\n" + "="*60)
print("CUSTOM MCP CONNECTION TEST")
print("="*60)
if not self.mcp_client.is_available:
print("❌ Custom MCP not available")
return False
print("✅ Custom MCP connected")
print(f"📋 Implementation: {self.mcp_client.implementation_type}")
# List available tools
tools = await self.mcp_client.list_tools()
print(f"📋 Found {len(tools)} tools")
if tools:
print("\nAvailable tools:")
for i, tool in enumerate(tools[:10], 1): # Show first 10
print(f" {i:2d}. {tool.name}")
if hasattr(tool, 'description'):
print(f" {tool.description}")
else:
print(" No tools available")
return len(tools) > 0
async def test_user_profile(self):
"""Test user profile retrieval"""
print("\n" + "="*60)
print("USER PROFILE TEST")
print("="*60)
# Check if tool exists
if not await self.mcp_client.has_tool("user_profile"):
print("❌ user_profile tool not available")
return None
print("✅ user_profile tool found")
try:
# Call user_profile tool
print("📞 Calling user_profile tool...")
profile_data = await self.mcp_client.call_tool("user_profile", {})
print("✅ User profile retrieved")
# Show profile summary
if isinstance(profile_data, dict):
display_name = profile_data.get('displayName', 'Unknown')
full_name = profile_data.get('fullName', 'Unknown')
user_name = profile_data.get('userName', 'Unknown')
print(f" Display Name: {display_name}")
print(f" Full Name: {full_name}")
print(f" Username: {user_name}")
print(f" Profile contains {len(profile_data)} fields")
return profile_data
except Exception as e:
print(f"❌ Error getting user profile: {e}")
logger.error(f"User profile error: {e}", exc_info=True)
return None
async def test_activities(self):
"""Test activities retrieval"""
print("\n" + "="*60)
print("ACTIVITIES TEST")
print("="*60)
if not await self.mcp_client.has_tool("get_activities"):
print("❌ get_activities tool not available")
return None
print("✅ get_activities tool found")
try:
print("📞 Calling get_activities (limit=5)...")
activities = await self.mcp_client.call_tool("get_activities", {"limit": 5})
if activities and len(activities) > 0:
print(f"✅ Retrieved {len(activities)} activities")
# Show activity summaries
print("\nRecent Activities:")
for i, activity in enumerate(activities[:3], 1):
activity_id = activity.get('activityId', 'Unknown')
activity_type = activity.get('activityType', {})
type_key = activity_type.get('typeKey', 'Unknown') if isinstance(activity_type, dict) else str(activity_type)
start_time = activity.get('startTimeLocal', 'Unknown time')
print(f" {i}. {type_key} (ID: {activity_id})")
print(f" Start: {start_time}")
# Try to get duration
duration = activity.get('duration')
if duration:
minutes = duration // 60
seconds = duration % 60
print(f" Duration: {minutes}m {seconds}s")
return activities
else:
print("📋 No activities found")
return []
except Exception as e:
print(f"❌ Error getting activities: {e}")
logger.error(f"Activities error: {e}", exc_info=True)
return None
async def test_activity_details(self, activities):
"""Test activity details retrieval"""
print("\n" + "="*60)
print("ACTIVITY DETAILS TEST")
print("="*60)
if not activities or len(activities) == 0:
print("❌ No activities available for details test")
return None
if not await self.mcp_client.has_tool("get_activity_details"):
print("❌ get_activity_details tool not available")
return None
# Get details for first activity
first_activity = activities[0]
activity_id = str(first_activity.get('activityId'))
print(f"📞 Getting details for activity {activity_id}...")
try:
details = await self.mcp_client.call_tool("get_activity_details", {
"activity_id": activity_id
})
print("✅ Activity details retrieved")
if isinstance(details, dict):
print(f" Details contain {len(details)} fields")
# Show some key details
activity_name = details.get('activityName', 'Unnamed')
sport = details.get('sportTypeId', 'Unknown sport')
distance = details.get('distance')
elapsed_duration = details.get('elapsedDuration')
print(f" Activity: {activity_name}")
print(f" Sport: {sport}")
if distance:
print(f" Distance: {distance/1000:.2f} km")
if elapsed_duration:
minutes = elapsed_duration // 60
seconds = elapsed_duration % 60
print(f" Duration: {minutes}m {seconds}s")
return details
except Exception as e:
print(f"❌ Error getting activity details: {e}")
logger.error(f"Activity details error: {e}", exc_info=True)
return None
async def test_daily_metrics(self):
"""Test daily metrics retrieval"""
print("\n" + "="*60)
print("DAILY METRICS TEST")
print("="*60)
metrics_to_test = ["daily_steps", "daily_sleep", "daily_stress"]
results = {}
for metric in metrics_to_test:
if await self.mcp_client.has_tool(metric):
try:
print(f"📞 Testing {metric}...")
data = await self.mcp_client.call_tool(metric, {"days": 1})
if data:
print(f"{metric} data retrieved")
if isinstance(data, list) and len(data) > 0:
print(f" Contains {len(data)} day(s) of data")
results[metric] = data
else:
print(f"⚠️ {metric} returned no data")
except Exception as e:
print(f"❌ Error with {metric}: {e}")
logger.debug(f"{metric} error: {e}")
else:
print(f"{metric} tool not available")
return results
async def test_cache_functionality(self):
"""Test cache functionality"""
print("\n" + "="*60)
print("CACHE FUNCTIONALITY TEST")
print("="*60)
if self.mcp_client.implementation_type != "custom_garth":
print("❌ Cache testing only available with custom implementation")
return False
try:
# Call user_profile twice to test caching
print("📞 First user_profile call (should hit API)...")
profile1 = await self.mcp_client.call_tool("user_profile", {})
print("📞 Second user_profile call (should hit cache)...")
profile2 = await self.mcp_client.call_tool("user_profile", {})
# Verify same data
if profile1 == profile2:
print("✅ Cache working correctly")
else:
print("⚠️ Cache data differs from API data")
# Show cache stats
cache_stats = self.mcp_client.get_cache_stats()
print(f"📊 Cache stats: {cache_stats}")
return True
except Exception as e:
print(f"❌ Cache test failed: {e}")
logger.error(f"Cache test error: {e}", exc_info=True)
return False
async def test_comprehensive_snapshot(self):
"""Test comprehensive data snapshot"""
print("\n" + "="*60)
print("COMPREHENSIVE SNAPSHOT TEST")
print("="*60)
if not await self.mcp_client.has_tool("snapshot"):
print("❌ snapshot tool not available")
return None
try:
print("📞 Getting comprehensive data snapshot...")
snapshot = await self.mcp_client.call_tool("snapshot", {
"start_date": "2024-09-20",
"end_date": "2024-09-24"
})
if isinstance(snapshot, dict):
print("✅ Snapshot retrieved successfully")
print(f" Snapshot contains {len(snapshot)} data categories:")
for category, data in snapshot.items():
if isinstance(data, list):
print(f" - {category}: {len(data)} items")
elif isinstance(data, dict):
print(f" - {category}: {len(data)} fields")
else:
print(f" - {category}: {type(data).__name__}")
return snapshot
except Exception as e:
print(f"❌ Snapshot test failed: {e}")
logger.error(f"Snapshot error: {e}", exc_info=True)
return None
async def run_all_tests(self):
"""Run comprehensive test suite"""
print("🚀 Starting Custom MCP Test Suite")
results = {}
# Test 1: Connection and tools
results['connection'] = await self.test_connection_and_tools()
if not results['connection']:
print("\n❌ Connection failed - skipping remaining tests")
return results
# Test 2: User profile
profile = await self.test_user_profile()
results['user_profile'] = profile is not None
# Test 3: Activities
activities = await self.test_activities()
results['activities'] = activities is not None
# Test 4: Activity details (if we have activities)
if activities and len(activities) > 0:
details = await self.test_activity_details(activities)
results['activity_details'] = details is not None
# Test 5: Daily metrics
metrics = await self.test_daily_metrics()
results['daily_metrics'] = len(metrics) > 0
# Test 6: Cache functionality
results['cache'] = await self.test_cache_functionality()
# Test 7: Comprehensive snapshot
snapshot = await self.test_comprehensive_snapshot()
results['snapshot'] = snapshot is not None
# Summary
self._print_test_summary(results)
return results
def _print_test_summary(self, results):
"""Print test summary"""
print("\n" + "="*60)
print("TEST SUMMARY")
print("="*60)
passed = 0
total = 0
for test_name, result in results.items():
total += 1
if result:
passed += 1
status = "✅ PASS"
else:
status = "❌ FAIL"
print(f"{test_name.replace('_', ' ').title():.<40} {status}")
print("-" * 60)
print(f"Total: {passed}/{total} tests passed")
if self.mcp_client.implementation_type == "custom_garth":
try:
cache_stats = self.mcp_client.get_cache_stats()
print(f"\nCache Status: {cache_stats.get('total_entries', 0)} entries")
for key in cache_stats.get('keys', []):
print(f" - {key}")
except Exception as e:
logger.debug(f"Could not get cache stats: {e}")
def validate_config(config: Config) -> bool:
"""Validate configuration"""
issues = []
if not config.garth_token:
issues.append("GARTH_TOKEN not set")
if issues:
print("❌ Configuration issues:")
for issue in issues:
print(f" - {issue}")
print("\nTo fix:")
print("1. Run 'pip install garth' to install Garth module")
print("2. Run authentication to get token")
print("3. Update config.yaml with your token")
return False
return True
async def main():
"""Main entry point"""
print("Custom MCP Test App - Test Custom Garth Implementation")
print("=" * 60)
try:
# Setup config
create_sample_config()
config = load_config()
if not validate_config(config):
return
# Create and run test app
app = CustomMCPTestApp(config)
try:
await app.initialize()
results = await app.run_all_tests()
# Exit with appropriate code
passed_tests = sum(1 for result in results.values() if result)
total_tests = len(results)
if passed_tests == total_tests:
print(f"\n🎉 All {total_tests} tests passed!")
sys.exit(0)
elif passed_tests > 0:
print(f"\n⚠️ {passed_tests}/{total_tests} tests passed")
sys.exit(1)
else:
print(f"\n❌ All {total_tests} tests failed")
sys.exit(1)
finally:
await app.cleanup()
except KeyboardInterrupt:
print("\n👋 Test interrupted")
except Exception as e:
print(f"\n💥 Test error: {e}")
logger.error(f"Main error: {e}", exc_info=True)
sys.exit(1)
if __name__ == "__main__":
asyncio.run(main())

263
tests/test_mcp_app.py Normal file
View File

@@ -0,0 +1,263 @@
#!/usr/bin/env python3
"""
Simple MCP Test App - Test MCP connection and user profile loading
"""
import asyncio
import json
import logging
import sys
from pathlib import Path
# Import our modules
from config import Config, load_config, create_sample_config
from mcp_client import MCPClient
from cache_manager import CacheManager
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
class MCPTestApp:
"""Simple test application for MCP functionality"""
def __init__(self, config: Config):
self.config = config
self.mcp_client = MCPClient(config)
self.cache_manager = CacheManager(default_ttl=300)
async def initialize(self):
"""Initialize MCP client"""
logger.info("Initializing MCP test app...")
await self.mcp_client.initialize()
async def cleanup(self):
"""Cleanup resources"""
await self.mcp_client.cleanup()
async def test_mcp_connection(self):
"""Test basic MCP connection and list tools"""
print("\n" + "="*60)
print("MCP CONNECTION TEST")
print("="*60)
if not self.mcp_client.is_available:
print("❌ MCP server not available")
return False
print("✅ MCP server connected")
# List available tools
tools = await self.mcp_client.list_tools()
print(f"📋 Found {len(tools)} tools:")
if tools:
for i, tool in enumerate(tools, 1):
print(f" {i}. {tool.name}")
if hasattr(tool, 'description') and tool.description:
print(f" {tool.description}")
else:
print(" No tools available")
return len(tools) > 0
async def test_user_profile(self):
"""Test user profile loading and caching"""
print("\n" + "="*60)
print("USER PROFILE TEST")
print("="*60)
# Check if user_profile tool is available
if not await self.mcp_client.has_tool("user_profile"):
print("❌ user_profile tool not available")
return None
print("✅ user_profile tool found")
try:
# Call user_profile tool
print("📞 Calling user_profile tool...")
profile_data = await self.mcp_client.call_tool("user_profile", {})
# Cache the profile
self.cache_manager.set("user_profile", profile_data, ttl=3600)
print("💾 User profile cached (TTL: 1 hour)")
# Pretty print the profile
print("\n" + "-"*40)
print("USER PROFILE DATA:")
print("-"*40)
print(json.dumps(profile_data, indent=2, default=str))
print("-"*40)
return profile_data
except Exception as e:
print(f"❌ Error getting user profile: {e}")
logger.error(f"User profile error: {e}", exc_info=True)
return None
async def test_cached_retrieval(self):
"""Test retrieving cached user profile"""
print("\n" + "="*60)
print("CACHE RETRIEVAL TEST")
print("="*60)
# Try to get cached profile
cached_profile = self.cache_manager.get("user_profile")
if cached_profile:
print("✅ User profile retrieved from cache")
print(f"📊 Cache stats: {self.cache_manager.get_stats()}")
return cached_profile
else:
print("❌ No cached user profile found")
return None
async def test_activities_preview(self):
"""Test getting recent activities if available"""
print("\n" + "="*60)
print("ACTIVITIES PREVIEW TEST")
print("="*60)
if not await self.mcp_client.has_tool("get_activities"):
print("❌ get_activities tool not available")
return None
print("✅ get_activities tool found")
try:
print("📞 Calling get_activities (limit=5)...")
activities = await self.mcp_client.call_tool("get_activities", {"limit": 5})
if activities:
print(f"📋 Retrieved {len(activities)} activities")
# Show basic info for each activity
print("\nRecent Activities:")
for i, activity in enumerate(activities[:3], 1): # Show first 3
activity_type = activity.get('activityType', {}).get('typeKey', 'Unknown')
start_time = activity.get('startTimeLocal', 'Unknown time')
duration = activity.get('duration', 0)
print(f" {i}. {activity_type} - {start_time}")
print(f" Duration: {duration // 60}m {duration % 60}s")
# Cache activities
self.cache_manager.set("recent_activities", activities, ttl=900)
print("💾 Activities cached (TTL: 15 minutes)")
return activities
else:
print("📋 No activities found")
return []
except Exception as e:
print(f"❌ Error getting activities: {e}")
logger.error(f"Activities error: {e}", exc_info=True)
return None
async def run_all_tests(self):
"""Run all tests in sequence"""
print("🚀 Starting MCP Test Suite")
results = {}
# Test 1: MCP Connection
results['mcp_connection'] = await self.test_mcp_connection()
if not results['mcp_connection']:
print("\n❌ MCP connection failed - skipping remaining tests")
return results
# Test 2: User Profile
results['user_profile'] = await self.test_user_profile()
# Test 3: Cache Retrieval
results['cache_retrieval'] = await self.test_cached_retrieval()
# Test 4: Activities Preview (optional)
results['activities'] = await self.test_activities_preview()
# Summary
print("\n" + "="*60)
print("TEST SUMMARY")
print("="*60)
for test_name, result in results.items():
status = "✅ PASS" if result else "❌ FAIL"
print(f"{test_name.replace('_', ' ').title()}: {status}")
# Cache summary
cache_stats = self.cache_manager.get_stats()
print(f"\nCache Status: {cache_stats['total_entries']} entries")
for key in cache_stats['keys']:
print(f" - {key}")
return results
def validate_config(config: Config) -> bool:
"""Validate configuration for MCP testing"""
issues = []
if not config.garth_token:
issues.append("GARTH_TOKEN not set")
if not config.garth_mcp_server_path:
issues.append("garth_mcp_server_path not set")
if issues:
print("❌ Configuration issues:")
for issue in issues:
print(f" - {issue}")
print("\nTo fix:")
print("1. Run 'uvx garth login' to get GARTH_TOKEN")
print("2. Install garth-mcp-server: 'npm install -g garth-mcp-server'")
print("3. Update config.yaml with your tokens")
return False
return True
async def main():
"""Main entry point"""
print("MCP Test App - Simple MCP and User Profile Test")
print("=" * 50)
try:
# Setup config
create_sample_config()
config = load_config()
if not validate_config(config):
return
# Create and run test app
app = MCPTestApp(config)
try:
await app.initialize()
results = await app.run_all_tests()
# Exit with appropriate code
if all(results.values()):
print("\n🎉 All tests passed!")
sys.exit(0)
else:
print("\n⚠️ Some tests failed")
sys.exit(1)
finally:
await app.cleanup()
except KeyboardInterrupt:
print("\n👋 Test interrupted")
except Exception as e:
print(f"\n💥 Test error: {e}")
logger.error(f"Main error: {e}", exc_info=True)
sys.exit(1)
if __name__ == "__main__":
asyncio.run(main())

View File

@@ -0,0 +1,20 @@
import asyncio
from config import load_config
from core_app import CyclingAnalyzerApp
async def main():
config = load_config()
app = CyclingAnalyzerApp(config, test_mode=True)
await app.initialize()
activity_data = app.cache_manager.get("last_cycling_details")
print("=== ACTIVITY DATA KEYS ===")
print(list(activity_data.keys()) if activity_data else "No activity data")
print("\nSample data:", dict(list(activity_data.items())[:10]) if activity_data else "No data")
result = await app.analyze_workout("analyze_last_workout")
print("=== TEST RESULT ===")
print(result)
await app.cleanup()
if __name__ == "__main__":
asyncio.run(main())