test_unified_mcp_integration.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. #!/usr/bin/env python3
  2. """
  3. Test MCP Integration with Unified Scraping
  4. Tests that the MCP server correctly handles unified configs.
  5. """
  6. import sys
  7. import os
  8. import json
  9. import tempfile
  10. import asyncio
  11. import pytest
  12. from pathlib import Path
  13. # WORKAROUND for shadowing issue: Temporarily change to /tmp to import external mcp
  14. # This avoids any local mcp/ directory being in the import path
  15. _original_dir = os.getcwd()
  16. MCP_AVAILABLE = False
  17. try:
  18. os.chdir('/tmp') # Change away from project directory
  19. from mcp.types import TextContent
  20. MCP_AVAILABLE = True
  21. except ImportError:
  22. pass
  23. finally:
  24. os.chdir(_original_dir) # Restore original directory
  25. # Configure pytest to only use asyncio backend (not trio)
  26. pytestmark = pytest.mark.anyio
  27. if MCP_AVAILABLE:
  28. from skill_seekers.mcp.server import validate_config_tool, scrape_docs_tool
  29. else:
  30. validate_config_tool = None
  31. scrape_docs_tool = None
  32. @pytest.mark.skipif(not MCP_AVAILABLE, reason="MCP package not installed")
  33. async def test_mcp_validate_unified_config():
  34. """Test that MCP can validate unified configs"""
  35. print("\n✓ Testing MCP validate_config_tool with unified config...")
  36. # Use existing unified config
  37. config_path = "configs/react_unified.json"
  38. if not Path(config_path).exists():
  39. print(f" ⚠️ Skipping: {config_path} not found")
  40. return
  41. args = {"config_path": config_path}
  42. result = await validate_config_tool(args)
  43. # Check result
  44. text = result[0].text
  45. assert "✅" in text, f"Expected success, got: {text}"
  46. assert "Unified" in text, f"Expected unified format detected, got: {text}"
  47. assert "Sources:" in text, f"Expected sources count, got: {text}"
  48. print(" ✅ MCP correctly validates unified config")
  49. @pytest.mark.skipif(not MCP_AVAILABLE, reason="MCP package not installed")
  50. async def test_mcp_validate_legacy_config():
  51. """Test that MCP can validate legacy configs"""
  52. print("\n✓ Testing MCP validate_config_tool with legacy config...")
  53. # Use existing legacy config
  54. config_path = "configs/react.json"
  55. if not Path(config_path).exists():
  56. print(f" ⚠️ Skipping: {config_path} not found")
  57. return
  58. args = {"config_path": config_path}
  59. result = await validate_config_tool(args)
  60. # Check result
  61. text = result[0].text
  62. assert "✅" in text, f"Expected success, got: {text}"
  63. assert "Legacy" in text, f"Expected legacy format detected, got: {text}"
  64. print(" ✅ MCP correctly validates legacy config")
  65. @pytest.mark.skipif(not MCP_AVAILABLE, reason="MCP package not installed")
  66. async def test_mcp_scrape_docs_detection():
  67. """Test that MCP scrape_docs correctly detects format"""
  68. print("\n✓ Testing MCP scrape_docs format detection...")
  69. # Create temporary unified config
  70. unified_config = {
  71. "name": "test_mcp_unified",
  72. "description": "Test unified via MCP",
  73. "merge_mode": "rule-based",
  74. "sources": [
  75. {
  76. "type": "documentation",
  77. "base_url": "https://example.com",
  78. "extract_api": True,
  79. "max_pages": 5
  80. }
  81. ]
  82. }
  83. with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
  84. json.dump(unified_config, f)
  85. unified_config_path = f.name
  86. # Create temporary legacy config
  87. legacy_config = {
  88. "name": "test_mcp_legacy",
  89. "description": "Test legacy via MCP",
  90. "base_url": "https://example.com",
  91. "max_pages": 5
  92. }
  93. with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
  94. json.dump(legacy_config, f)
  95. legacy_config_path = f.name
  96. try:
  97. # Test unified detection
  98. with open(unified_config_path, 'r') as f:
  99. config = json.load(f)
  100. is_unified = 'sources' in config and isinstance(config['sources'], list)
  101. assert is_unified, "Should detect unified format"
  102. print(" ✅ Unified format detected correctly")
  103. # Test legacy detection
  104. with open(legacy_config_path, 'r') as f:
  105. config = json.load(f)
  106. is_unified = 'sources' in config and isinstance(config['sources'], list)
  107. assert not is_unified, "Should detect legacy format"
  108. print(" ✅ Legacy format detected correctly")
  109. finally:
  110. # Cleanup
  111. Path(unified_config_path).unlink(missing_ok=True)
  112. Path(legacy_config_path).unlink(missing_ok=True)
  113. @pytest.mark.skipif(not MCP_AVAILABLE, reason="MCP package not installed")
  114. async def test_mcp_merge_mode_override():
  115. """Test that MCP can override merge mode"""
  116. print("\n✓ Testing MCP merge_mode override...")
  117. # Create unified config
  118. config = {
  119. "name": "test_merge_override",
  120. "description": "Test merge mode override",
  121. "merge_mode": "rule-based",
  122. "sources": [
  123. {"type": "documentation", "base_url": "https://example.com"}
  124. ]
  125. }
  126. with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
  127. json.dump(config, f)
  128. config_path = f.name
  129. try:
  130. # Test that we can override merge_mode in args
  131. args = {
  132. "config_path": config_path,
  133. "merge_mode": "claude-enhanced" # Override
  134. }
  135. # Check that args has merge_mode
  136. assert args.get("merge_mode") == "claude-enhanced"
  137. print(" ✅ Merge mode override supported")
  138. finally:
  139. Path(config_path).unlink(missing_ok=True)
  140. # Run all tests
  141. async def run_all_tests():
  142. print("=" * 60)
  143. print("MCP Unified Scraping Integration Tests")
  144. print("=" * 60)
  145. try:
  146. await test_mcp_validate_unified_config()
  147. await test_mcp_validate_legacy_config()
  148. await test_mcp_scrape_docs_detection()
  149. await test_mcp_merge_mode_override()
  150. print("\n" + "=" * 60)
  151. print("✅ All MCP integration tests passed!")
  152. print("=" * 60)
  153. except AssertionError as e:
  154. print(f"\n❌ Test failed: {e}")
  155. sys.exit(1)
  156. except Exception as e:
  157. print(f"\n❌ Unexpected error: {e}")
  158. import traceback
  159. traceback.print_exc()
  160. sys.exit(1)
  161. if __name__ == '__main__':
  162. asyncio.run(run_all_tests())