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
200 lines
7.2 KiB
Python
200 lines
7.2 KiB
Python
import unittest
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
from semantica.ontology import (
|
|
OntologyEngine,
|
|
ClassInferrer,
|
|
PropertyGenerator,
|
|
OntologyOptimizer,
|
|
CompetencyQuestionsManager,
|
|
LLMOntologyGenerator
|
|
)
|
|
from semantica.visualization import OntologyVisualizer
|
|
|
|
pytestmark = pytest.mark.integration
|
|
|
|
class TestNotebook14(unittest.TestCase):
|
|
"""
|
|
Tests mirroring the steps in cookbook/introduction/14_Ontology.ipynb
|
|
to ensure the documented examples work correctly.
|
|
"""
|
|
|
|
def _run_full_pipeline(self):
|
|
"""Helper to run the full pipeline and return the ontology."""
|
|
engine = OntologyEngine(base_uri="https://docs.semantica.dev/ontology/")
|
|
|
|
# Sample Data
|
|
entities = [
|
|
{"id": "e1", "type": "Company", "name": "TechCorp", "founded": "2010"},
|
|
{"id": "e2", "type": "Person", "name": "Alice", "role": "CEO"},
|
|
{"id": "e3", "type": "Person", "name": "Bob", "role": "CTO"},
|
|
{"id": "e4", "type": "Department", "name": "Engineering"},
|
|
{"id": "e5", "type": "Project", "name": "Project Phoenix"}
|
|
]
|
|
|
|
relationships = [
|
|
{"source": "e2", "target": "e1", "type": "leads"},
|
|
{"source": "e3", "target": "e4", "type": "manages"},
|
|
{"source": "e4", "target": "e1", "type": "part_of"},
|
|
{"source": "e3", "target": "e5", "type": "works_on"}
|
|
]
|
|
|
|
data = {
|
|
"entities": entities,
|
|
"relationships": relationships
|
|
}
|
|
|
|
# Run the full pipeline
|
|
ontology = engine.from_data(data, name="CorporateOntology", min_occurrences=1)
|
|
return ontology
|
|
|
|
def test_full_pipeline(self):
|
|
"""Test the 6-stage generation pipeline with sample data."""
|
|
ontology = self._run_full_pipeline()
|
|
|
|
# Verification
|
|
self.assertEqual(ontology['name'], "CorporateOntology")
|
|
self.assertGreater(len(ontology['classes']), 0)
|
|
self.assertGreater(len(ontology['properties']), 0)
|
|
|
|
# Inspect Classes (just to ensure no errors in access)
|
|
for cls in ontology['classes']:
|
|
self.assertIn('name', cls)
|
|
self.assertIn('uri', cls)
|
|
|
|
# Inspect Properties
|
|
for prop in ontology['properties']:
|
|
self.assertIn('name', prop)
|
|
self.assertIn('type', prop)
|
|
|
|
def _run_class_inferrer(self):
|
|
"""Helper to run class inference and return classes."""
|
|
inferrer = ClassInferrer(min_occurrences=1)
|
|
|
|
raw_entities = [
|
|
{"type": "Manager", "name": "Dave", "level": 5},
|
|
{"type": "Manager", "name": "Eve", "level": 4},
|
|
{"type": "Employee", "name": "Frank"},
|
|
{"type": "TemporaryWorker", "name": "Grace"}
|
|
]
|
|
|
|
classes = inferrer.infer_classes(raw_entities, build_hierarchy=True)
|
|
return classes
|
|
|
|
def test_class_inferrer(self):
|
|
"""Test ClassInferrer usage."""
|
|
classes = self._run_class_inferrer()
|
|
|
|
self.assertGreater(len(classes), 0)
|
|
class_names = [c['name'] for c in classes]
|
|
self.assertIn("Manager", class_names)
|
|
self.assertIn("Employee", class_names)
|
|
|
|
def test_property_generator(self):
|
|
"""Test PropertyGenerator usage."""
|
|
# Setup context classes (reusing logic from previous test)
|
|
classes = self._run_class_inferrer()
|
|
|
|
prop_gen = PropertyGenerator()
|
|
|
|
complex_entities = [
|
|
{"id": "m1", "type": "Manager", "name": "Dave", "level": 5},
|
|
{"id": "e1", "type": "Employee", "name": "Frank"}
|
|
]
|
|
complex_relationships = [
|
|
{"source": "m1", "target": "e1", "type": "supervises"}
|
|
]
|
|
|
|
properties = prop_gen.infer_properties(
|
|
entities=complex_entities,
|
|
relationships=complex_relationships,
|
|
classes=classes,
|
|
min_occurrences=1
|
|
)
|
|
|
|
self.assertGreater(len(properties), 0)
|
|
prop_names = [p['name'] for p in properties]
|
|
# "level" should be a data property, "supervises" an object property
|
|
self.assertTrue(any("level" in p['name'].lower() for p in properties))
|
|
self.assertTrue(any("supervises" in p['name'].lower() for p in properties))
|
|
|
|
def test_ontology_optimizer(self):
|
|
"""Test OntologyOptimizer usage."""
|
|
optimizer = OntologyOptimizer()
|
|
|
|
messy_ontology = {
|
|
"classes": [
|
|
{"name": "Person", "uri": "http://example.org/Person"},
|
|
{"name": "Person", "uri": "http://example.org/Person"} # Duplicate!
|
|
],
|
|
"properties": []
|
|
}
|
|
|
|
clean_ontology = optimizer.optimize_ontology(messy_ontology, remove_redundancy=True)
|
|
|
|
self.assertEqual(len(messy_ontology['classes']), 2)
|
|
self.assertEqual(len(clean_ontology['classes']), 1)
|
|
|
|
@patch("semantica.visualization.ontology_visualizer.make_subplots")
|
|
@patch("semantica.visualization.ontology_visualizer.go")
|
|
def test_visualization(self, mock_go, mock_make_subplots):
|
|
"""Test OntologyVisualizer usage (mocking plotly)."""
|
|
viz = OntologyVisualizer()
|
|
ontology = self._run_full_pipeline()
|
|
|
|
# Mock figures
|
|
mock_fig = MagicMock()
|
|
mock_go.Figure.return_value = mock_fig
|
|
mock_make_subplots.return_value = mock_fig
|
|
mock_go.Scatter.return_value = MagicMock()
|
|
mock_go.Indicator.return_value = MagicMock()
|
|
|
|
# 1. Interactive Class Hierarchy
|
|
fig_hierarchy = viz.visualize_hierarchy(ontology, output="interactive")
|
|
# Just check it didn't crash; real test would check calls
|
|
|
|
# 2. Ontology Structure Network
|
|
fig_structure = viz.visualize_structure(ontology, output="interactive")
|
|
|
|
# 3. Metrics Dashboard
|
|
fig_metrics = viz.visualize_metrics(ontology, output="interactive")
|
|
|
|
@patch("semantica.ontology.llm_generator.LLMOntologyGenerator.generate_ontology_from_text")
|
|
def test_llm_ontology_generator(self, mock_generate):
|
|
"""Test LLMOntologyGenerator (mocked)."""
|
|
mock_generate.return_value = {
|
|
"classes": [{"name": "Department"}, {"name": "Course"}],
|
|
"properties": [],
|
|
"name": "UniversityOntology"
|
|
}
|
|
|
|
llm_gen = LLMOntologyGenerator(provider="openai", model="gpt-4")
|
|
|
|
text_description = "A University has many Departments."
|
|
|
|
llm_ontology = llm_gen.generate_ontology_from_text(
|
|
text=text_description,
|
|
name="UniversityOntology"
|
|
)
|
|
|
|
self.assertEqual(llm_ontology['name'], "UniversityOntology")
|
|
self.assertEqual(len(llm_ontology['classes']), 2)
|
|
|
|
def test_competency_questions(self):
|
|
"""Test CompetencyQuestionsManager."""
|
|
cq_manager = CompetencyQuestionsManager()
|
|
|
|
cq_manager.add_question("Who is the CEO?", category="general")
|
|
questions = cq_manager.questions
|
|
self.assertGreater(len(questions), 0)
|
|
|
|
def test_ontology_engine_initialization(self):
|
|
"""Test initializing the OntologyEngine."""
|
|
engine = OntologyEngine(base_uri="https://docs.semantica.dev/ontology/")
|
|
self.assertIsNotNone(engine)
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|