mirror of
https://github.com/semantica-agi/semantica.git
synced 2026-09-10 04:00:35 +00:00
- Robust ID extraction in CentralityCalculator, CommunityDetector, and ConnectivityAnalyzer - Support for direct Entity objects and dictionaries as node identifiers - Improved Entity hashability in utils/types.py - Added integration test to verify fix and prevent regression
217 lines
9.2 KiB
Python
217 lines
9.2 KiB
Python
import pytest
|
|
import json
|
|
from unittest.mock import MagicMock, patch
|
|
from semantica.ingest import MCPIngestor, ingest_mcp, DBIngestor, FileIngestor
|
|
from semantica.ingest.mcp_ingestor import MCPData
|
|
|
|
pytestmark = pytest.mark.integration
|
|
|
|
class TestCookbookIntegration:
|
|
|
|
@pytest.fixture
|
|
def mock_mcp_server(self):
|
|
# We need to patch both httpx and requests because MCPClient tries httpx first
|
|
with patch("httpx.post") as mock_httpx_post, \
|
|
patch("requests.post") as mock_requests_post:
|
|
|
|
def side_effect(url, json=None, **kwargs):
|
|
if not json:
|
|
return MagicMock()
|
|
|
|
method = json.get("method")
|
|
response_mock = MagicMock()
|
|
response_mock.status_code = 200
|
|
|
|
if method == "initialize":
|
|
response_mock.json.return_value = {
|
|
"jsonrpc": "2.0",
|
|
"id": json.get("id"),
|
|
"result": {
|
|
"protocolVersion": "2024-11-05",
|
|
"capabilities": {},
|
|
"serverInfo": {"name": "test_server", "version": "1.0"}
|
|
}
|
|
}
|
|
elif method == "resources/list":
|
|
response_mock.json.return_value = {
|
|
"jsonrpc": "2.0",
|
|
"id": json.get("id"),
|
|
"result": {
|
|
"resources": [
|
|
{"uri": "resource://test/1", "name": "Test Resource 1", "description": "Desc 1"},
|
|
{"uri": "resource://test/2", "name": "Test Resource 2", "description": "Desc 2"},
|
|
{"uri": "resource://inventory/database", "name": "Inventory DB", "description": "Inventory"}
|
|
]
|
|
}
|
|
}
|
|
elif method == "tools/list":
|
|
response_mock.json.return_value = {
|
|
"jsonrpc": "2.0",
|
|
"id": json.get("id"),
|
|
"result": {
|
|
"tools": [
|
|
{"name": "test_tool_1", "description": "Tool 1", "inputSchema": {}},
|
|
{"name": "test_tool_2", "description": "Tool 2", "inputSchema": {}},
|
|
{"name": "query_inventory", "description": "Query Inventory", "inputSchema": {}}
|
|
]
|
|
}
|
|
}
|
|
elif method == "resources/read":
|
|
response_mock.json.return_value = {
|
|
"jsonrpc": "2.0",
|
|
"id": json.get("id"),
|
|
"result": {
|
|
"contents": [
|
|
{"uri": json.get("params", {}).get("uri"), "text": "Sample content"}
|
|
]
|
|
}
|
|
}
|
|
elif method == "tools/call":
|
|
tool_name = json.get("params", {}).get("name")
|
|
content = [{"type": "text", "text": "Tool Output"}]
|
|
|
|
if tool_name == "query_inventory":
|
|
content = [{"type": "text", "text": '{"warehouse_id": "WH001", "level": 100}'}]
|
|
|
|
response_mock.json.return_value = {
|
|
"jsonrpc": "2.0",
|
|
"id": json.get("id"),
|
|
"result": {
|
|
"content": content
|
|
}
|
|
}
|
|
else:
|
|
response_mock.json.return_value = {
|
|
"jsonrpc": "2.0",
|
|
"id": json.get("id"),
|
|
"result": {}
|
|
}
|
|
|
|
return response_mock
|
|
|
|
mock_httpx_post.side_effect = side_effect
|
|
mock_requests_post.side_effect = side_effect
|
|
yield mock_httpx_post
|
|
|
|
def test_financial_data_integration(self, mock_mcp_server):
|
|
"""
|
|
Validates the logic from cookbook/use_cases/finance/01_Financial_Data_Integration.ipynb
|
|
"""
|
|
# 1. Initialize MCP ingestor
|
|
mcp_ingestor = MCPIngestor()
|
|
|
|
# 2. Connect to financial data MCP server
|
|
financial_mcp_url = "http://localhost:8000/mcp"
|
|
|
|
# Patching progress tracker to avoid console output issues during testing if needed
|
|
# But MCPIngestor now handles it gracefully or we can let it run.
|
|
# We need to mock get_progress_tracker to avoid 'NoneType' errors if not initialized properly in some envs
|
|
# although my previous fixes should handle it. Let's patch it to be safe and clean.
|
|
with patch("semantica.ingest.mcp_ingestor.get_progress_tracker") as mock_tracker:
|
|
tracker_instance = MagicMock()
|
|
mock_tracker.return_value = tracker_instance
|
|
|
|
mcp_ingestor.connect(
|
|
"financial_server",
|
|
url=financial_mcp_url,
|
|
headers={"Authorization": "Bearer token"}
|
|
)
|
|
|
|
# 3. List available resources
|
|
resources = mcp_ingestor.list_available_resources("financial_server")
|
|
assert len(resources) >= 2
|
|
assert resources[0].name == "Test Resource 1"
|
|
|
|
# 4. List available tools
|
|
tools = mcp_ingestor.list_available_tools("financial_server")
|
|
assert len(tools) >= 2
|
|
assert tools[0].name == "test_tool_1"
|
|
|
|
# 5. Ingest resources (simulating notebook logic)
|
|
# The notebook likely calls ingest_resources
|
|
ingested_data = mcp_ingestor.ingest_resources(
|
|
"financial_server",
|
|
resource_uris=["resource://test/1"]
|
|
)
|
|
assert len(ingested_data) == 1
|
|
# content is the raw result from MCP read_resource
|
|
assert ingested_data[0].content["contents"][0]["text"] == "Sample content"
|
|
|
|
def test_supply_chain_data_integration(self, mock_mcp_server):
|
|
"""
|
|
Validates the logic from cookbook/use_cases/supply_chain/01_Supply_Chain_Data_Integration.ipynb
|
|
"""
|
|
mcp_ingestor = MCPIngestor()
|
|
supply_chain_mcp_url = "http://localhost:8000/mcp"
|
|
|
|
with patch("semantica.ingest.mcp_ingestor.get_progress_tracker") as mock_tracker:
|
|
tracker_instance = MagicMock()
|
|
mock_tracker.return_value = tracker_instance
|
|
|
|
mcp_ingestor.connect(
|
|
"supply_chain_server",
|
|
url=supply_chain_mcp_url,
|
|
headers={"Authorization": "Bearer token"}
|
|
)
|
|
|
|
# Resource ingestion
|
|
inventory_data = mcp_ingestor.ingest_resources(
|
|
"supply_chain_server",
|
|
resource_uris=["resource://inventory/database"]
|
|
)
|
|
assert len(inventory_data) == 1
|
|
|
|
# Tool ingestion
|
|
inventory_levels = mcp_ingestor.ingest_tool_output(
|
|
"supply_chain_server",
|
|
tool_name="query_inventory",
|
|
arguments={"warehouse_id": "WH001"}
|
|
)
|
|
assert inventory_levels is not None
|
|
# Based on my mock, it returns a dict with 'content'
|
|
if isinstance(inventory_levels, MCPData):
|
|
assert inventory_levels.content is not None
|
|
elif isinstance(inventory_levels, dict):
|
|
assert "content" in inventory_levels
|
|
else:
|
|
# Should be list or MCPData
|
|
assert isinstance(inventory_levels, list)
|
|
|
|
def test_medical_database_integration(self, mock_mcp_server):
|
|
"""
|
|
Validates the logic from cookbook/use_cases/healthcare/05_Medical_Database_Integration.ipynb
|
|
"""
|
|
mcp_ingestor = MCPIngestor()
|
|
medical_mcp_url = "http://localhost:8000/mcp"
|
|
|
|
with patch("semantica.ingest.mcp_ingestor.get_progress_tracker") as mock_tracker:
|
|
tracker_instance = MagicMock()
|
|
mock_tracker.return_value = tracker_instance
|
|
|
|
mcp_ingestor.connect(
|
|
"medical_server",
|
|
url=medical_mcp_url
|
|
)
|
|
|
|
resources = mcp_ingestor.list_available_resources("medical_server")
|
|
assert len(resources) > 0
|
|
|
|
def test_threat_intelligence_integration(self, mock_mcp_server):
|
|
"""
|
|
Validates the logic from cookbook/use_cases/cybersecurity/05_Threat_Intelligence_Integration.ipynb
|
|
"""
|
|
mcp_ingestor = MCPIngestor()
|
|
threat_mcp_url = "http://localhost:8000/mcp"
|
|
|
|
with patch("semantica.ingest.mcp_ingestor.get_progress_tracker") as mock_tracker:
|
|
tracker_instance = MagicMock()
|
|
mock_tracker.return_value = tracker_instance
|
|
|
|
mcp_ingestor.connect(
|
|
"threat_server",
|
|
url=threat_mcp_url
|
|
)
|
|
|
|
tools = mcp_ingestor.list_available_tools("threat_server")
|
|
assert len(tools) > 0
|