mirror of
https://github.com/semantica-agi/semantica.git
synced 2026-08-29 04:26:20 +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
150 lines
6.3 KiB
Python
150 lines
6.3 KiB
Python
import unittest
|
|
from unittest.mock import MagicMock, patch
|
|
import sys
|
|
import numpy as np
|
|
|
|
# Helper to mock modules
|
|
def mock_module(name):
|
|
m = MagicMock()
|
|
sys.modules[name] = m
|
|
return m
|
|
|
|
class TestOptionalDependencies(unittest.TestCase):
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
# Mock heavy/problematic dependencies globally to prevent environment crashes
|
|
# We use a dict to save original modules if they exist, but for this test file
|
|
# we generally want to run in a controlled "clean" environment.
|
|
cls.modules_to_patch = [
|
|
'sklearn', 'sklearn.decomposition', 'sklearn.manifold',
|
|
'scipy', 'scipy.optimize',
|
|
'matplotlib', 'matplotlib.pyplot', 'matplotlib.patches',
|
|
'plotly', 'plotly.express', 'plotly.graph_objects', 'plotly.subplots',
|
|
'networkx', 'seaborn'
|
|
]
|
|
|
|
cls.original_modules = {}
|
|
for mod in cls.modules_to_patch:
|
|
if mod in sys.modules:
|
|
cls.original_modules[mod] = sys.modules[mod]
|
|
sys.modules[mod] = MagicMock()
|
|
|
|
@classmethod
|
|
def tearDownClass(cls):
|
|
# Restore original modules
|
|
for mod in cls.modules_to_patch:
|
|
if mod in cls.original_modules:
|
|
sys.modules[mod] = cls.original_modules[mod]
|
|
else:
|
|
del sys.modules[mod]
|
|
|
|
def setUp(self):
|
|
# Clear cached visualization modules to ensure fresh imports
|
|
self.viz_modules = [
|
|
'semantica.visualization.embedding_visualizer',
|
|
'semantica.visualization.ontology_visualizer',
|
|
'semantica.visualization.kg_visualizer',
|
|
'semantica.visualization.utils.export_formats'
|
|
]
|
|
for mod in self.viz_modules:
|
|
if mod in sys.modules:
|
|
del sys.modules[mod]
|
|
|
|
def test_embedding_visualizer_without_umap(self):
|
|
"""Test EmbeddingVisualizer behavior when umap is missing."""
|
|
# Ensure umap is missing
|
|
with patch.dict(sys.modules, {'umap': None}):
|
|
from semantica.visualization.embedding_visualizer import EmbeddingVisualizer
|
|
|
|
# Setup PCA mock to verify fallback
|
|
mock_pca_class = sys.modules['sklearn.decomposition'].PCA
|
|
mock_pca_instance = mock_pca_class.return_value
|
|
# Configure fit_transform to return correct shape (n_samples, 2)
|
|
mock_pca_instance.fit_transform.return_value = np.zeros((4, 2))
|
|
|
|
viz = EmbeddingVisualizer()
|
|
# Use numpy array!
|
|
embeddings = np.array([[0, 1, 2], [1, 0, 3], [0, 0, 0], [1, 1, 1]])
|
|
|
|
# Should fallback to PCA when method="umap" is used but umap is None
|
|
# The code logs a warning and uses PCA
|
|
viz.visualize_2d_projection(embeddings, method="umap")
|
|
|
|
# Verify PCA was called
|
|
mock_pca_class.assert_called()
|
|
|
|
def test_ontology_visualizer_without_graphviz(self):
|
|
"""Test OntologyVisualizer behavior when graphviz is missing."""
|
|
# Ensure graphviz is missing
|
|
with patch.dict(sys.modules, {'graphviz': None}):
|
|
from semantica.visualization.ontology_visualizer import OntologyVisualizer, ProcessingError
|
|
|
|
viz = OntologyVisualizer()
|
|
ontology = {
|
|
"classes": [
|
|
{"name": "A", "label": "A"},
|
|
{"name": "B", "label": "B", "parent": "A"}
|
|
]
|
|
}
|
|
|
|
with self.assertRaises(ProcessingError) as cm:
|
|
viz.visualize_hierarchy(ontology, output="dot", file_path="test.dot")
|
|
|
|
self.assertIn("Graphviz is required for DOT export", str(cm.exception))
|
|
|
|
def test_analytics_visualizer_without_plotly(self):
|
|
"""Test AnalyticsVisualizer behavior when plotly is missing."""
|
|
with patch.dict(sys.modules, {'plotly': None, 'plotly.express': None, 'plotly.graph_objects': None}):
|
|
from semantica.visualization.analytics_visualizer import AnalyticsVisualizer, ProcessingError
|
|
|
|
# Need to ensure numpy is available for init (it's imported at top level)
|
|
# But we are testing plotly missing.
|
|
|
|
viz = AnalyticsVisualizer()
|
|
|
|
with self.assertRaises(ProcessingError) as cm:
|
|
viz.visualize_centrality_rankings({"node1": 1.0})
|
|
|
|
self.assertIn("Plotly is required", str(cm.exception))
|
|
|
|
def test_analytics_visualizer_without_plotly(self):
|
|
"""Test AnalyticsVisualizer behavior when plotly is missing."""
|
|
with patch.dict(sys.modules, {'plotly': None, 'plotly.express': None, 'plotly.graph_objects': None}):
|
|
from semantica.visualization.analytics_visualizer import AnalyticsVisualizer, ProcessingError
|
|
|
|
viz = AnalyticsVisualizer()
|
|
|
|
with self.assertRaises(ProcessingError) as cm:
|
|
viz.visualize_centrality_rankings({})
|
|
|
|
self.assertIn("Plotly is required", str(cm.exception))
|
|
|
|
def test_semantic_network_visualizer_without_plotly(self):
|
|
"""Test SemanticNetworkVisualizer behavior when plotly is missing."""
|
|
with patch.dict(sys.modules, {'plotly': None, 'plotly.express': None, 'plotly.graph_objects': None}):
|
|
from semantica.visualization.semantic_network_visualizer import SemanticNetworkVisualizer, ProcessingError
|
|
|
|
viz = SemanticNetworkVisualizer()
|
|
|
|
with self.assertRaises(ProcessingError) as cm:
|
|
viz.visualize_network({})
|
|
|
|
self.assertIn("Plotly is required", str(cm.exception))
|
|
|
|
def test_temporal_visualizer_without_plotly(self):
|
|
"""Test TemporalVisualizer behavior when plotly is missing."""
|
|
with patch.dict(sys.modules, {'plotly': None, 'plotly.express': None, 'plotly.graph_objects': None}):
|
|
from semantica.visualization.temporal_visualizer import TemporalVisualizer, ProcessingError
|
|
|
|
viz = TemporalVisualizer()
|
|
|
|
with self.assertRaises(ProcessingError) as cm:
|
|
viz.visualize_timeline({"events": []})
|
|
|
|
self.assertIn("Plotly is required", str(cm.exception))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|