mirror of
https://github.com/semantica-agi/semantica.git
synced 2026-08-29 04:26:20 +00:00
Merge pull request #77 from Hawksight-AI/ontology
Comprehensive Testing and Bug Fixes for Ontology Module
This commit is contained in:
@@ -31,7 +31,7 @@ class LLMOntologyGenerator:
|
||||
)
|
||||
|
||||
base_uri = options.get("base_uri")
|
||||
name = options.get("name") or "GeneratedOntology"
|
||||
name = options.get("name")
|
||||
version = options.get("version") or "1.0"
|
||||
|
||||
prompt = self._build_prompt(text=text, name=name, base_uri=base_uri)
|
||||
|
||||
@@ -216,6 +216,10 @@ class NamespaceManager:
|
||||
|
||||
def _to_camel_case(self, name: str) -> str:
|
||||
"""Convert name to camelCase."""
|
||||
# Check if already likely camelCase (starts with lower, has upper, single word)
|
||||
if name and name[0].islower() and any(c.isupper() for c in name) and ' ' not in name and '_' not in name:
|
||||
return name
|
||||
|
||||
# Remove special characters and split
|
||||
words = re.findall(r"[a-zA-Z0-9]+", name)
|
||||
if not words:
|
||||
|
||||
@@ -262,8 +262,8 @@ class NamingConventions:
|
||||
# camelCase for object properties
|
||||
suggested = self._to_camel_case(name)
|
||||
else:
|
||||
# lowercase for data properties
|
||||
suggested = name.lower()
|
||||
# camelCase for data properties as well (standard practice)
|
||||
suggested = self._to_camel_case(name)
|
||||
|
||||
return suggested
|
||||
|
||||
@@ -364,6 +364,10 @@ class NamingConventions:
|
||||
|
||||
def _to_camel_case(self, name: str) -> str:
|
||||
"""Convert to camelCase."""
|
||||
# Check if already likely camelCase (starts with lower, has upper, single word)
|
||||
if name and name[0].islower() and any(c.isupper() for c in name) and ' ' not in name and '_' not in name:
|
||||
return name
|
||||
|
||||
words = re.findall(r"[a-zA-Z0-9]+", name)
|
||||
if not words:
|
||||
return "hasProperty"
|
||||
@@ -381,8 +385,8 @@ class NamingConventions:
|
||||
# Basic singularization rules
|
||||
if name.lower().endswith("ies"):
|
||||
return name[:-3] + "y"
|
||||
elif name.lower().endswith("es"):
|
||||
elif name.lower().endswith("es") and not name.lower().endswith("ss"):
|
||||
return name[:-2]
|
||||
elif name.lower().endswith("s") and len(name) > 1:
|
||||
elif name.lower().endswith("s") and len(name) > 1 and not name.lower().endswith("ss") and name.lower() not in ["class", "process", "analysis"]:
|
||||
return name[:-1]
|
||||
return name
|
||||
|
||||
@@ -170,7 +170,13 @@ class OntologyGenerator:
|
||||
self.progress_tracker.update_tracking(
|
||||
tracking_id, message="Stage 3: Mapping to OWL types..."
|
||||
)
|
||||
typed_definitions = self._stage3_definition_to_types(definitions, **options)
|
||||
|
||||
# Ensure entities and relationships are available for property inference
|
||||
stage3_options = options.copy()
|
||||
stage3_options["entities"] = data.get("entities", [])
|
||||
stage3_options["relationships"] = data.get("relationships", [])
|
||||
|
||||
typed_definitions = self._stage3_definition_to_types(definitions, **stage3_options)
|
||||
|
||||
# Stage 4: Hierarchy Generation
|
||||
self.progress_tracker.update_tracking(
|
||||
@@ -266,9 +272,14 @@ class OntologyGenerator:
|
||||
relationships = options.get("relationships", [])
|
||||
entities = options.get("entities", [])
|
||||
|
||||
# Clean options for infer_properties to avoid multiple values for arguments
|
||||
prop_options = options.copy()
|
||||
prop_options.pop("entities", None)
|
||||
prop_options.pop("relationships", None)
|
||||
|
||||
# Infer properties
|
||||
properties = self.property_generator.infer_properties(
|
||||
entities=entities, relationships=relationships, classes=classes, **options
|
||||
entities=entities, relationships=relationships, classes=classes, **prop_options
|
||||
)
|
||||
|
||||
# Add types to classes
|
||||
|
||||
@@ -89,6 +89,11 @@ class PropertyGenerator:
|
||||
submodule="PropertyGenerator",
|
||||
message=f"Inferring properties from {len(entities)} entities and {len(relationships)} relationships",
|
||||
)
|
||||
|
||||
# Merge config into options
|
||||
for key, value in self.config.items():
|
||||
if key not in options:
|
||||
options[key] = value
|
||||
|
||||
try:
|
||||
properties = []
|
||||
|
||||
@@ -302,14 +302,22 @@ class OntologyVisualizer:
|
||||
# Add domain edges
|
||||
domain = prop.get("domain")
|
||||
if domain:
|
||||
edges.append({"source": prop_name, "target": domain, "type": "domain"})
|
||||
if isinstance(domain, list):
|
||||
for d in domain:
|
||||
edges.append({"source": prop_name, "target": d, "type": "domain"})
|
||||
else:
|
||||
edges.append({"source": prop_name, "target": domain, "type": "domain"})
|
||||
|
||||
# Add range edges
|
||||
range_val = prop.get("range")
|
||||
if range_val:
|
||||
edges.append(
|
||||
{"source": prop_name, "target": range_val, "type": "range"}
|
||||
)
|
||||
if isinstance(range_val, list):
|
||||
for r in range_val:
|
||||
edges.append({"source": prop_name, "target": r, "type": "range"})
|
||||
else:
|
||||
edges.append(
|
||||
{"source": prop_name, "target": range_val, "type": "range"}
|
||||
)
|
||||
|
||||
return self._visualize_structure_plotly(
|
||||
nodes, edges, output, file_path, **options
|
||||
|
||||
@@ -0,0 +1,209 @@
|
||||
import unittest
|
||||
from unittest.mock import MagicMock, patch
|
||||
from semantica.ontology import (
|
||||
OntologyEngine,
|
||||
ClassInferrer,
|
||||
PropertyGenerator,
|
||||
OntologyOptimizer,
|
||||
OntologyValidator,
|
||||
CompetencyQuestionsManager,
|
||||
LLMOntologyGenerator
|
||||
)
|
||||
from semantica.visualization import OntologyVisualizer
|
||||
|
||||
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)
|
||||
|
||||
def test_ontology_validator(self):
|
||||
"""Test OntologyValidator usage."""
|
||||
validator = OntologyValidator(
|
||||
check_consistency=False, # Skip reasoner for unit test speed/dependency
|
||||
check_satisfiability=False
|
||||
)
|
||||
|
||||
ontology = self._run_full_pipeline()
|
||||
result = validator.validate_ontology(ontology)
|
||||
|
||||
self.assertTrue(result.valid)
|
||||
# consistent might be None if check skipped, or True/False.
|
||||
# Just check it runs without error.
|
||||
|
||||
@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()
|
||||
@@ -0,0 +1,262 @@
|
||||
|
||||
import unittest
|
||||
from unittest.mock import MagicMock, patch
|
||||
from collections import defaultdict
|
||||
from semantica.ontology.class_inferrer import ClassInferrer
|
||||
from semantica.ontology.property_generator import PropertyGenerator
|
||||
from semantica.ontology.naming_conventions import NamingConventions
|
||||
from semantica.ontology.ontology_generator import OntologyGenerator
|
||||
from semantica.ontology.ontology_validator import OntologyValidator, ValidationResult
|
||||
from semantica.ontology.namespace_manager import NamespaceManager
|
||||
from semantica.ontology.module_manager import ModuleManager
|
||||
|
||||
class TestOntologyComprehensive(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
# Mock dependencies
|
||||
self.mock_logger = MagicMock()
|
||||
self.mock_tracker = MagicMock()
|
||||
self.mock_tracker.start_tracking.return_value = "track_id"
|
||||
|
||||
# Patch loggers and trackers
|
||||
self.patchers = [
|
||||
patch('semantica.ontology.class_inferrer.get_logger', return_value=self.mock_logger),
|
||||
patch('semantica.ontology.class_inferrer.get_progress_tracker', return_value=self.mock_tracker),
|
||||
patch('semantica.ontology.property_generator.get_logger', return_value=self.mock_logger),
|
||||
patch('semantica.ontology.property_generator.get_progress_tracker', return_value=self.mock_tracker),
|
||||
patch('semantica.ontology.naming_conventions.get_logger', return_value=self.mock_logger),
|
||||
patch('semantica.ontology.naming_conventions.get_progress_tracker', return_value=self.mock_tracker),
|
||||
patch('semantica.ontology.ontology_generator.get_logger', return_value=self.mock_logger),
|
||||
patch('semantica.ontology.ontology_generator.get_progress_tracker', return_value=self.mock_tracker),
|
||||
patch('semantica.ontology.ontology_validator.get_logger', return_value=self.mock_logger),
|
||||
patch('semantica.ontology.ontology_validator.get_progress_tracker', return_value=self.mock_tracker),
|
||||
]
|
||||
|
||||
for p in self.patchers:
|
||||
p.start()
|
||||
|
||||
def tearDown(self):
|
||||
for p in self.patchers:
|
||||
p.stop()
|
||||
|
||||
# --- NamingConventions Tests ---
|
||||
def test_naming_conventions(self):
|
||||
nc = NamingConventions()
|
||||
|
||||
# Test class naming (PascalCase)
|
||||
self.assertEqual(nc.normalize_class_name("person"), "Person")
|
||||
self.assertEqual(nc.normalize_class_name("my class"), "MyClass")
|
||||
self.assertEqual(nc.normalize_class_name("MY_CLASS"), "MyClass")
|
||||
|
||||
# Test property naming (camelCase)
|
||||
self.assertEqual(nc.normalize_property_name("has name", "data"), "hasName")
|
||||
self.assertEqual(nc.normalize_property_name("is related to", "object"), "isRelatedTo")
|
||||
|
||||
# Test validation
|
||||
is_valid, _ = nc.validate_class_name("Person")
|
||||
self.assertTrue(is_valid)
|
||||
|
||||
is_valid, _ = nc.validate_property_name("hasName", "data")
|
||||
self.assertTrue(is_valid)
|
||||
|
||||
# --- ClassInferrer Tests ---
|
||||
def test_class_inferrer(self):
|
||||
inferrer = ClassInferrer(min_occurrences=1)
|
||||
|
||||
entities = [
|
||||
{"type": "Person", "name": "Alice", "age": 30},
|
||||
{"type": "Person", "name": "Bob", "age": 25},
|
||||
{"type": "Organization", "name": "Acme Corp", "location": "US"}
|
||||
]
|
||||
|
||||
classes = inferrer.infer_classes(entities)
|
||||
|
||||
self.assertEqual(len(classes), 2)
|
||||
|
||||
person_class = next(c for c in classes if c["name"] == "Person")
|
||||
org_class = next(c for c in classes if c["name"] == "Organization")
|
||||
|
||||
self.assertEqual(person_class["entity_count"], 2)
|
||||
self.assertEqual(org_class["entity_count"], 1)
|
||||
|
||||
# Check inferred properties in class definition metadata
|
||||
# (Implementation detail: infer_classes calls _create_class_from_entities)
|
||||
# We might need to check if properties are in metadata or top level
|
||||
# Based on docstring: properties: List of common property names
|
||||
self.assertIn("name", person_class["properties"])
|
||||
self.assertIn("age", person_class["properties"])
|
||||
|
||||
def test_class_inferrer_min_occurrences(self):
|
||||
inferrer = ClassInferrer(min_occurrences=2)
|
||||
|
||||
entities = [
|
||||
{"type": "Person", "name": "Alice"},
|
||||
{"type": "Person", "name": "Bob"},
|
||||
{"type": "RareEntity", "name": "Rare"}
|
||||
]
|
||||
|
||||
classes = inferrer.infer_classes(entities)
|
||||
|
||||
self.assertEqual(len(classes), 1)
|
||||
self.assertEqual(classes[0]["name"], "Person")
|
||||
|
||||
# --- PropertyGenerator Tests ---
|
||||
def test_property_generator(self):
|
||||
# Test property inference logic
|
||||
generator = PropertyGenerator(min_occurrences=1)
|
||||
|
||||
entities = [{"id": "p1", "type": "Person"}, {"id": "o1", "type": "Organization"}]
|
||||
relationships = [
|
||||
{"source_id": "p1", "target_id": "o1", "type": "worksFor", "source_type": "Person", "target_type": "Organization"}
|
||||
]
|
||||
classes = [{"name": "Person"}, {"name": "Organization"}]
|
||||
|
||||
properties = generator.infer_properties(entities, relationships, classes)
|
||||
|
||||
# Debug print
|
||||
# print(f"Properties: {properties}")
|
||||
|
||||
# Check object property
|
||||
works_for = next((p for p in properties if p["name"] == "worksFor"), None)
|
||||
self.assertIsNotNone(works_for)
|
||||
|
||||
# --- OntologyGenerator Tests ---
|
||||
def test_ontology_generator_pipeline(self):
|
||||
# Test full pipeline with mocks
|
||||
generator = OntologyGenerator()
|
||||
|
||||
# Mock dependencies
|
||||
generator.class_inferrer.infer_classes = MagicMock(return_value=[
|
||||
{"name": "Person", "uri": "http://example.org/Person"}
|
||||
])
|
||||
generator.property_generator.infer_properties = MagicMock(return_value=[
|
||||
{"name": "worksFor", "type": "object", "domain": ["Person"], "range": ["Organization"]}
|
||||
])
|
||||
|
||||
data = {
|
||||
"entities": [{"type": "Person", "id": "p1"}],
|
||||
"relationships": [{"type": "worksFor", "source": "p1"}]
|
||||
}
|
||||
|
||||
ontology = generator.generate_ontology(data, name="TestOntology")
|
||||
|
||||
self.assertEqual(ontology["name"], "TestOntology")
|
||||
self.assertIn("classes", ontology)
|
||||
self.assertIn("properties", ontology)
|
||||
|
||||
# --- OWLGenerator Tests ---
|
||||
def test_owl_generator(self):
|
||||
try:
|
||||
from semantica.ontology.owl_generator import OWLGenerator
|
||||
except ImportError:
|
||||
self.skipTest("OWLGenerator not importable")
|
||||
|
||||
generator = OWLGenerator()
|
||||
ontology = {
|
||||
"name": "TestOntology",
|
||||
"uri": "http://example.org/ontology",
|
||||
"classes": [{"name": "Person", "uri": "http://example.org/ontology/Person"}],
|
||||
"properties": [{"name": "hasName", "type": "data", "uri": "http://example.org/ontology/hasName"}]
|
||||
}
|
||||
|
||||
owl_output = generator.generate_owl(ontology, format="turtle")
|
||||
self.assertIsInstance(owl_output, str)
|
||||
self.assertIn("Person", owl_output)
|
||||
self.assertIn("hasName", owl_output)
|
||||
|
||||
# --- OntologyValidator Tests ---
|
||||
def test_ontology_validator(self):
|
||||
try:
|
||||
from semantica.ontology.ontology_validator import OntologyValidator, ValidationResult
|
||||
except ImportError:
|
||||
self.skipTest("OntologyValidator not importable")
|
||||
|
||||
validator = OntologyValidator(reasoner="auto") # or mock reasoner
|
||||
ontology = {
|
||||
"name": "TestOntology",
|
||||
"classes": [{"name": "Person", "parent": "Entity"}]
|
||||
}
|
||||
|
||||
# Without owlready2, it might just return valid=True (default) or fail gracefully
|
||||
# If owlready2 is missing, it should handle it.
|
||||
# Let's check basic structure validation if any.
|
||||
result = validator.validate_ontology(ontology)
|
||||
self.assertIsInstance(result, ValidationResult)
|
||||
|
||||
# --- LLMOntologyGenerator Tests ---
|
||||
def test_llm_ontology_generator(self):
|
||||
try:
|
||||
from semantica.ontology.llm_generator import LLMOntologyGenerator
|
||||
except ImportError:
|
||||
self.skipTest("LLMOntologyGenerator not importable")
|
||||
|
||||
# Mock provider
|
||||
with patch('semantica.ontology.llm_generator.create_provider') as mock_create:
|
||||
mock_provider = MagicMock()
|
||||
mock_create.return_value = mock_provider
|
||||
|
||||
# Setup mock return
|
||||
mock_provider.generate_structured.return_value = {
|
||||
"name": "AI Generated",
|
||||
"classes": [{"name": "Robot", "label": "A Robot"}],
|
||||
"properties": [{"name": "hasModel", "type": "data"}]
|
||||
}
|
||||
|
||||
generator = LLMOntologyGenerator(provider="openai")
|
||||
ontology = generator.generate_ontology_from_text("Create ontology about robots")
|
||||
|
||||
self.assertEqual(ontology["name"], "AI Generated")
|
||||
self.assertEqual(len(ontology["classes"]), 1)
|
||||
self.assertEqual(ontology["classes"][0]["name"], "Robot")
|
||||
|
||||
# --- OntologyEngine Tests ---
|
||||
def test_ontology_engine(self):
|
||||
try:
|
||||
from semantica.ontology.engine import OntologyEngine
|
||||
except ImportError:
|
||||
self.skipTest("OntologyEngine not importable")
|
||||
|
||||
engine = OntologyEngine()
|
||||
|
||||
# Mock internal components
|
||||
engine.generator.generate_ontology = MagicMock(return_value={"name": "EngineOntology"})
|
||||
|
||||
ontology = engine.from_data({"entities": []})
|
||||
self.assertEqual(ontology["name"], "EngineOntology")
|
||||
|
||||
# --- NamespaceManager Tests ---
|
||||
def test_namespace_manager(self):
|
||||
nm = NamespaceManager(base_uri="http://example.org/")
|
||||
|
||||
iri = nm.generate_class_iri("Person")
|
||||
self.assertEqual(iri, "http://example.org/Person")
|
||||
|
||||
prop_iri = nm.generate_property_iri("hasName")
|
||||
# With fix, it should preserve hasName
|
||||
self.assertEqual(prop_iri, "http://example.org/hasName")
|
||||
|
||||
# bind_prefix is not in NamespaceManager, checking code...
|
||||
# It's register_namespace
|
||||
nm.register_namespace("ex", "http://example.org/")
|
||||
self.assertEqual(nm.get_namespace("ex"), "http://example.org/")
|
||||
|
||||
# --- ModuleManager Tests ---
|
||||
def test_module_manager(self):
|
||||
mm = ModuleManager()
|
||||
|
||||
module_def = {
|
||||
"name": "PersonModule",
|
||||
"classes": ["Person"],
|
||||
"properties": ["hasName"]
|
||||
}
|
||||
|
||||
# ModuleManager uses create_module
|
||||
mm.create_module("PersonModule", "http://example.org/person", classes=["Person"], properties=["hasName"])
|
||||
self.assertIn("PersonModule", mm.modules)
|
||||
|
||||
mod = mm.get_module("PersonModule")
|
||||
self.assertEqual(mod.name, "PersonModule")
|
||||
self.assertIn("Person", mod.classes)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user