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()