mirror of
https://github.com/trustgraph-ai/trustgraph.git
synced 2026-04-25 08:26:21 +02:00
Merge 2.0 to master (#651)
This commit is contained in:
parent
3666ece2c5
commit
b9d7bf9a8b
212 changed files with 13940 additions and 6180 deletions
|
|
@ -7,7 +7,7 @@ collecting labels and definitions for entity embedding and retrieval.
|
|||
|
||||
import pytest
|
||||
from trustgraph.extract.kg.ontology.extract import Processor
|
||||
from trustgraph.schema.core.primitives import Triple, Value
|
||||
from trustgraph.schema.core.primitives import Triple, Term, IRI, LITERAL
|
||||
from trustgraph.schema.knowledge.graph import EntityContext
|
||||
|
||||
|
||||
|
|
@ -25,9 +25,9 @@ class TestEntityContextBuilding:
|
|||
"""Test that entity context is built from rdfs:label."""
|
||||
triples = [
|
||||
Triple(
|
||||
s=Value(value="https://example.com/entity/cornish-pasty", is_uri=True),
|
||||
p=Value(value="http://www.w3.org/2000/01/rdf-schema#label", is_uri=True),
|
||||
o=Value(value="Cornish Pasty", is_uri=False)
|
||||
s=Term(type=IRI, iri="https://example.com/entity/cornish-pasty"),
|
||||
p=Term(type=IRI, iri="http://www.w3.org/2000/01/rdf-schema#label"),
|
||||
o=Term(type=LITERAL, value="Cornish Pasty")
|
||||
)
|
||||
]
|
||||
|
||||
|
|
@ -35,16 +35,16 @@ class TestEntityContextBuilding:
|
|||
|
||||
assert len(contexts) == 1, "Should create one entity context"
|
||||
assert isinstance(contexts[0], EntityContext)
|
||||
assert contexts[0].entity.value == "https://example.com/entity/cornish-pasty"
|
||||
assert contexts[0].entity.iri == "https://example.com/entity/cornish-pasty"
|
||||
assert "Label: Cornish Pasty" in contexts[0].context
|
||||
|
||||
def test_builds_context_from_definition(self, processor):
|
||||
"""Test that entity context includes definitions."""
|
||||
triples = [
|
||||
Triple(
|
||||
s=Value(value="https://example.com/entity/pasty", is_uri=True),
|
||||
p=Value(value="http://www.w3.org/2004/02/skos/core#definition", is_uri=True),
|
||||
o=Value(value="A baked pastry filled with savory ingredients", is_uri=False)
|
||||
s=Term(type=IRI, iri="https://example.com/entity/pasty"),
|
||||
p=Term(type=IRI, iri="http://www.w3.org/2004/02/skos/core#definition"),
|
||||
o=Term(type=LITERAL, value="A baked pastry filled with savory ingredients")
|
||||
)
|
||||
]
|
||||
|
||||
|
|
@ -57,14 +57,14 @@ class TestEntityContextBuilding:
|
|||
"""Test that label and definition are combined in context."""
|
||||
triples = [
|
||||
Triple(
|
||||
s=Value(value="https://example.com/entity/recipe1", is_uri=True),
|
||||
p=Value(value="http://www.w3.org/2000/01/rdf-schema#label", is_uri=True),
|
||||
o=Value(value="Pasty Recipe", is_uri=False)
|
||||
s=Term(type=IRI, iri="https://example.com/entity/recipe1"),
|
||||
p=Term(type=IRI, iri="http://www.w3.org/2000/01/rdf-schema#label"),
|
||||
o=Term(type=LITERAL, value="Pasty Recipe")
|
||||
),
|
||||
Triple(
|
||||
s=Value(value="https://example.com/entity/recipe1", is_uri=True),
|
||||
p=Value(value="http://www.w3.org/2004/02/skos/core#definition", is_uri=True),
|
||||
o=Value(value="Traditional Cornish pastry recipe", is_uri=False)
|
||||
s=Term(type=IRI, iri="https://example.com/entity/recipe1"),
|
||||
p=Term(type=IRI, iri="http://www.w3.org/2004/02/skos/core#definition"),
|
||||
o=Term(type=LITERAL, value="Traditional Cornish pastry recipe")
|
||||
)
|
||||
]
|
||||
|
||||
|
|
@ -80,14 +80,14 @@ class TestEntityContextBuilding:
|
|||
"""Test that only the first label is used in context."""
|
||||
triples = [
|
||||
Triple(
|
||||
s=Value(value="https://example.com/entity/food1", is_uri=True),
|
||||
p=Value(value="http://www.w3.org/2000/01/rdf-schema#label", is_uri=True),
|
||||
o=Value(value="First Label", is_uri=False)
|
||||
s=Term(type=IRI, iri="https://example.com/entity/food1"),
|
||||
p=Term(type=IRI, iri="http://www.w3.org/2000/01/rdf-schema#label"),
|
||||
o=Term(type=LITERAL, value="First Label")
|
||||
),
|
||||
Triple(
|
||||
s=Value(value="https://example.com/entity/food1", is_uri=True),
|
||||
p=Value(value="http://www.w3.org/2000/01/rdf-schema#label", is_uri=True),
|
||||
o=Value(value="Second Label", is_uri=False)
|
||||
s=Term(type=IRI, iri="https://example.com/entity/food1"),
|
||||
p=Term(type=IRI, iri="http://www.w3.org/2000/01/rdf-schema#label"),
|
||||
o=Term(type=LITERAL, value="Second Label")
|
||||
)
|
||||
]
|
||||
|
||||
|
|
@ -101,14 +101,14 @@ class TestEntityContextBuilding:
|
|||
"""Test that all definitions are included in context."""
|
||||
triples = [
|
||||
Triple(
|
||||
s=Value(value="https://example.com/entity/food1", is_uri=True),
|
||||
p=Value(value="http://www.w3.org/2004/02/skos/core#definition", is_uri=True),
|
||||
o=Value(value="First definition", is_uri=False)
|
||||
s=Term(type=IRI, iri="https://example.com/entity/food1"),
|
||||
p=Term(type=IRI, iri="http://www.w3.org/2004/02/skos/core#definition"),
|
||||
o=Term(type=LITERAL, value="First definition")
|
||||
),
|
||||
Triple(
|
||||
s=Value(value="https://example.com/entity/food1", is_uri=True),
|
||||
p=Value(value="http://www.w3.org/2004/02/skos/core#definition", is_uri=True),
|
||||
o=Value(value="Second definition", is_uri=False)
|
||||
s=Term(type=IRI, iri="https://example.com/entity/food1"),
|
||||
p=Term(type=IRI, iri="http://www.w3.org/2004/02/skos/core#definition"),
|
||||
o=Term(type=LITERAL, value="Second definition")
|
||||
)
|
||||
]
|
||||
|
||||
|
|
@ -123,9 +123,9 @@ class TestEntityContextBuilding:
|
|||
"""Test that schema.org description is treated as definition."""
|
||||
triples = [
|
||||
Triple(
|
||||
s=Value(value="https://example.com/entity/food1", is_uri=True),
|
||||
p=Value(value="https://schema.org/description", is_uri=True),
|
||||
o=Value(value="A delicious food item", is_uri=False)
|
||||
s=Term(type=IRI, iri="https://example.com/entity/food1"),
|
||||
p=Term(type=IRI, iri="https://schema.org/description"),
|
||||
o=Term(type=LITERAL, value="A delicious food item")
|
||||
)
|
||||
]
|
||||
|
||||
|
|
@ -138,26 +138,26 @@ class TestEntityContextBuilding:
|
|||
"""Test that contexts are created for multiple entities."""
|
||||
triples = [
|
||||
Triple(
|
||||
s=Value(value="https://example.com/entity/entity1", is_uri=True),
|
||||
p=Value(value="http://www.w3.org/2000/01/rdf-schema#label", is_uri=True),
|
||||
o=Value(value="Entity One", is_uri=False)
|
||||
s=Term(type=IRI, iri="https://example.com/entity/entity1"),
|
||||
p=Term(type=IRI, iri="http://www.w3.org/2000/01/rdf-schema#label"),
|
||||
o=Term(type=LITERAL, value="Entity One")
|
||||
),
|
||||
Triple(
|
||||
s=Value(value="https://example.com/entity/entity2", is_uri=True),
|
||||
p=Value(value="http://www.w3.org/2000/01/rdf-schema#label", is_uri=True),
|
||||
o=Value(value="Entity Two", is_uri=False)
|
||||
s=Term(type=IRI, iri="https://example.com/entity/entity2"),
|
||||
p=Term(type=IRI, iri="http://www.w3.org/2000/01/rdf-schema#label"),
|
||||
o=Term(type=LITERAL, value="Entity Two")
|
||||
),
|
||||
Triple(
|
||||
s=Value(value="https://example.com/entity/entity3", is_uri=True),
|
||||
p=Value(value="http://www.w3.org/2000/01/rdf-schema#label", is_uri=True),
|
||||
o=Value(value="Entity Three", is_uri=False)
|
||||
s=Term(type=IRI, iri="https://example.com/entity/entity3"),
|
||||
p=Term(type=IRI, iri="http://www.w3.org/2000/01/rdf-schema#label"),
|
||||
o=Term(type=LITERAL, value="Entity Three")
|
||||
)
|
||||
]
|
||||
|
||||
contexts = processor.build_entity_contexts(triples)
|
||||
|
||||
assert len(contexts) == 3, "Should create context for each entity"
|
||||
entity_uris = [ctx.entity.value for ctx in contexts]
|
||||
entity_uris = [ctx.entity.iri for ctx in contexts]
|
||||
assert "https://example.com/entity/entity1" in entity_uris
|
||||
assert "https://example.com/entity/entity2" in entity_uris
|
||||
assert "https://example.com/entity/entity3" in entity_uris
|
||||
|
|
@ -166,9 +166,9 @@ class TestEntityContextBuilding:
|
|||
"""Test that URI objects are ignored (only literal labels/definitions)."""
|
||||
triples = [
|
||||
Triple(
|
||||
s=Value(value="https://example.com/entity/food1", is_uri=True),
|
||||
p=Value(value="http://www.w3.org/2000/01/rdf-schema#label", is_uri=True),
|
||||
o=Value(value="https://example.com/some/uri", is_uri=True) # URI, not literal
|
||||
s=Term(type=IRI, iri="https://example.com/entity/food1"),
|
||||
p=Term(type=IRI, iri="http://www.w3.org/2000/01/rdf-schema#label"),
|
||||
o=Term(type=IRI, iri="https://example.com/some/uri") # URI, not literal
|
||||
)
|
||||
]
|
||||
|
||||
|
|
@ -181,14 +181,14 @@ class TestEntityContextBuilding:
|
|||
"""Test that other predicates are ignored."""
|
||||
triples = [
|
||||
Triple(
|
||||
s=Value(value="https://example.com/entity/food1", is_uri=True),
|
||||
p=Value(value="http://www.w3.org/1999/02/22-rdf-syntax-ns#type", is_uri=True),
|
||||
o=Value(value="http://example.com/Food", is_uri=True)
|
||||
s=Term(type=IRI, iri="https://example.com/entity/food1"),
|
||||
p=Term(type=IRI, iri="http://www.w3.org/1999/02/22-rdf-syntax-ns#type"),
|
||||
o=Term(type=IRI, iri="http://example.com/Food")
|
||||
),
|
||||
Triple(
|
||||
s=Value(value="https://example.com/entity/food1", is_uri=True),
|
||||
p=Value(value="http://example.com/produces", is_uri=True),
|
||||
o=Value(value="https://example.com/entity/food2", is_uri=True)
|
||||
s=Term(type=IRI, iri="https://example.com/entity/food1"),
|
||||
p=Term(type=IRI, iri="http://example.com/produces"),
|
||||
o=Term(type=IRI, iri="https://example.com/entity/food2")
|
||||
)
|
||||
]
|
||||
|
||||
|
|
@ -205,29 +205,29 @@ class TestEntityContextBuilding:
|
|||
|
||||
assert len(contexts) == 0, "Empty triple list should return empty contexts"
|
||||
|
||||
def test_entity_context_has_value_object(self, processor):
|
||||
"""Test that EntityContext.entity is a Value object."""
|
||||
def test_entity_context_has_term_object(self, processor):
|
||||
"""Test that EntityContext.entity is a Term object."""
|
||||
triples = [
|
||||
Triple(
|
||||
s=Value(value="https://example.com/entity/test", is_uri=True),
|
||||
p=Value(value="http://www.w3.org/2000/01/rdf-schema#label", is_uri=True),
|
||||
o=Value(value="Test Entity", is_uri=False)
|
||||
s=Term(type=IRI, iri="https://example.com/entity/test"),
|
||||
p=Term(type=IRI, iri="http://www.w3.org/2000/01/rdf-schema#label"),
|
||||
o=Term(type=LITERAL, value="Test Entity")
|
||||
)
|
||||
]
|
||||
|
||||
contexts = processor.build_entity_contexts(triples)
|
||||
|
||||
assert len(contexts) == 1
|
||||
assert isinstance(contexts[0].entity, Value), "Entity should be Value object"
|
||||
assert contexts[0].entity.is_uri, "Entity should be marked as URI"
|
||||
assert isinstance(contexts[0].entity, Term), "Entity should be Term object"
|
||||
assert contexts[0].entity.type == IRI, "Entity should be IRI type"
|
||||
|
||||
def test_entity_context_text_is_string(self, processor):
|
||||
"""Test that EntityContext.context is a string."""
|
||||
triples = [
|
||||
Triple(
|
||||
s=Value(value="https://example.com/entity/test", is_uri=True),
|
||||
p=Value(value="http://www.w3.org/2000/01/rdf-schema#label", is_uri=True),
|
||||
o=Value(value="Test Entity", is_uri=False)
|
||||
s=Term(type=IRI, iri="https://example.com/entity/test"),
|
||||
p=Term(type=IRI, iri="http://www.w3.org/2000/01/rdf-schema#label"),
|
||||
o=Term(type=LITERAL, value="Test Entity")
|
||||
)
|
||||
]
|
||||
|
||||
|
|
@ -241,22 +241,22 @@ class TestEntityContextBuilding:
|
|||
triples = [
|
||||
# Entity with label - should create context
|
||||
Triple(
|
||||
s=Value(value="https://example.com/entity/entity1", is_uri=True),
|
||||
p=Value(value="http://www.w3.org/2000/01/rdf-schema#label", is_uri=True),
|
||||
o=Value(value="Entity One", is_uri=False)
|
||||
s=Term(type=IRI, iri="https://example.com/entity/entity1"),
|
||||
p=Term(type=IRI, iri="http://www.w3.org/2000/01/rdf-schema#label"),
|
||||
o=Term(type=LITERAL, value="Entity One")
|
||||
),
|
||||
# Entity with only rdf:type - should NOT create context
|
||||
Triple(
|
||||
s=Value(value="https://example.com/entity/entity2", is_uri=True),
|
||||
p=Value(value="http://www.w3.org/1999/02/22-rdf-syntax-ns#type", is_uri=True),
|
||||
o=Value(value="http://example.com/Food", is_uri=True)
|
||||
s=Term(type=IRI, iri="https://example.com/entity/entity2"),
|
||||
p=Term(type=IRI, iri="http://www.w3.org/1999/02/22-rdf-syntax-ns#type"),
|
||||
o=Term(type=IRI, iri="http://example.com/Food")
|
||||
)
|
||||
]
|
||||
|
||||
contexts = processor.build_entity_contexts(triples)
|
||||
|
||||
assert len(contexts) == 1, "Should only create context for entity with label/definition"
|
||||
assert contexts[0].entity.value == "https://example.com/entity/entity1"
|
||||
assert contexts[0].entity.iri == "https://example.com/entity/entity1"
|
||||
|
||||
|
||||
class TestEntityContextEdgeCases:
|
||||
|
|
@ -266,9 +266,9 @@ class TestEntityContextEdgeCases:
|
|||
"""Test handling of unicode characters in labels."""
|
||||
triples = [
|
||||
Triple(
|
||||
s=Value(value="https://example.com/entity/café", is_uri=True),
|
||||
p=Value(value="http://www.w3.org/2000/01/rdf-schema#label", is_uri=True),
|
||||
o=Value(value="Café Spécial", is_uri=False)
|
||||
s=Term(type=IRI, iri="https://example.com/entity/café"),
|
||||
p=Term(type=IRI, iri="http://www.w3.org/2000/01/rdf-schema#label"),
|
||||
o=Term(type=LITERAL, value="Café Spécial")
|
||||
)
|
||||
]
|
||||
|
||||
|
|
@ -282,9 +282,9 @@ class TestEntityContextEdgeCases:
|
|||
long_def = "This is a very long definition " * 50
|
||||
triples = [
|
||||
Triple(
|
||||
s=Value(value="https://example.com/entity/test", is_uri=True),
|
||||
p=Value(value="http://www.w3.org/2004/02/skos/core#definition", is_uri=True),
|
||||
o=Value(value=long_def, is_uri=False)
|
||||
s=Term(type=IRI, iri="https://example.com/entity/test"),
|
||||
p=Term(type=IRI, iri="http://www.w3.org/2004/02/skos/core#definition"),
|
||||
o=Term(type=LITERAL, value=long_def)
|
||||
)
|
||||
]
|
||||
|
||||
|
|
@ -297,9 +297,9 @@ class TestEntityContextEdgeCases:
|
|||
"""Test handling of special characters in context text."""
|
||||
triples = [
|
||||
Triple(
|
||||
s=Value(value="https://example.com/entity/test", is_uri=True),
|
||||
p=Value(value="http://www.w3.org/2000/01/rdf-schema#label", is_uri=True),
|
||||
o=Value(value="Test & Entity <with> \"quotes\"", is_uri=False)
|
||||
s=Term(type=IRI, iri="https://example.com/entity/test"),
|
||||
p=Term(type=IRI, iri="http://www.w3.org/2000/01/rdf-schema#label"),
|
||||
o=Term(type=LITERAL, value="Test & Entity <with> \"quotes\"")
|
||||
)
|
||||
]
|
||||
|
||||
|
|
@ -313,27 +313,27 @@ class TestEntityContextEdgeCases:
|
|||
triples = [
|
||||
# Label - relevant
|
||||
Triple(
|
||||
s=Value(value="https://example.com/entity/recipe1", is_uri=True),
|
||||
p=Value(value="http://www.w3.org/2000/01/rdf-schema#label", is_uri=True),
|
||||
o=Value(value="Cornish Pasty Recipe", is_uri=False)
|
||||
s=Term(type=IRI, iri="https://example.com/entity/recipe1"),
|
||||
p=Term(type=IRI, iri="http://www.w3.org/2000/01/rdf-schema#label"),
|
||||
o=Term(type=LITERAL, value="Cornish Pasty Recipe")
|
||||
),
|
||||
# Type - irrelevant
|
||||
Triple(
|
||||
s=Value(value="https://example.com/entity/recipe1", is_uri=True),
|
||||
p=Value(value="http://www.w3.org/1999/02/22-rdf-syntax-ns#type", is_uri=True),
|
||||
o=Value(value="http://example.com/Recipe", is_uri=True)
|
||||
s=Term(type=IRI, iri="https://example.com/entity/recipe1"),
|
||||
p=Term(type=IRI, iri="http://www.w3.org/1999/02/22-rdf-syntax-ns#type"),
|
||||
o=Term(type=IRI, iri="http://example.com/Recipe")
|
||||
),
|
||||
# Property - irrelevant
|
||||
Triple(
|
||||
s=Value(value="https://example.com/entity/recipe1", is_uri=True),
|
||||
p=Value(value="http://example.com/produces", is_uri=True),
|
||||
o=Value(value="https://example.com/entity/pasty", is_uri=True)
|
||||
s=Term(type=IRI, iri="https://example.com/entity/recipe1"),
|
||||
p=Term(type=IRI, iri="http://example.com/produces"),
|
||||
o=Term(type=IRI, iri="https://example.com/entity/pasty")
|
||||
),
|
||||
# Definition - relevant
|
||||
Triple(
|
||||
s=Value(value="https://example.com/entity/recipe1", is_uri=True),
|
||||
p=Value(value="http://www.w3.org/2004/02/skos/core#definition", is_uri=True),
|
||||
o=Value(value="Traditional British pastry recipe", is_uri=False)
|
||||
s=Term(type=IRI, iri="https://example.com/entity/recipe1"),
|
||||
p=Term(type=IRI, iri="http://www.w3.org/2004/02/skos/core#definition"),
|
||||
o=Term(type=LITERAL, value="Traditional British pastry recipe")
|
||||
)
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ the knowledge graph.
|
|||
import pytest
|
||||
from trustgraph.extract.kg.ontology.extract import Processor
|
||||
from trustgraph.extract.kg.ontology.ontology_selector import OntologySubset
|
||||
from trustgraph.schema.core.primitives import Triple, Value
|
||||
from trustgraph.schema.core.primitives import Triple, Term, IRI, LITERAL
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
|
@ -92,12 +92,12 @@ class TestOntologyTripleGeneration:
|
|||
# Find type triples for Recipe class
|
||||
recipe_type_triples = [
|
||||
t for t in triples
|
||||
if t.s.value == "http://purl.org/ontology/fo/Recipe"
|
||||
and t.p.value == "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"
|
||||
if t.s.iri == "http://purl.org/ontology/fo/Recipe"
|
||||
and t.p.iri == "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"
|
||||
]
|
||||
|
||||
assert len(recipe_type_triples) == 1, "Should generate exactly one type triple per class"
|
||||
assert recipe_type_triples[0].o.value == "http://www.w3.org/2002/07/owl#Class", \
|
||||
assert recipe_type_triples[0].o.iri == "http://www.w3.org/2002/07/owl#Class", \
|
||||
"Class type should be owl:Class"
|
||||
|
||||
def test_generates_class_labels(self, extractor, sample_ontology_subset):
|
||||
|
|
@ -107,14 +107,14 @@ class TestOntologyTripleGeneration:
|
|||
# Find label triples for Recipe class
|
||||
recipe_label_triples = [
|
||||
t for t in triples
|
||||
if t.s.value == "http://purl.org/ontology/fo/Recipe"
|
||||
and t.p.value == "http://www.w3.org/2000/01/rdf-schema#label"
|
||||
if t.s.iri == "http://purl.org/ontology/fo/Recipe"
|
||||
and t.p.iri == "http://www.w3.org/2000/01/rdf-schema#label"
|
||||
]
|
||||
|
||||
assert len(recipe_label_triples) == 1, "Should generate label triple for class"
|
||||
assert recipe_label_triples[0].o.value == "Recipe", \
|
||||
"Label should match class label from ontology"
|
||||
assert not recipe_label_triples[0].o.is_uri, \
|
||||
assert recipe_label_triples[0].o.type == LITERAL, \
|
||||
"Label should be a literal, not URI"
|
||||
|
||||
def test_generates_class_comments(self, extractor, sample_ontology_subset):
|
||||
|
|
@ -124,8 +124,8 @@ class TestOntologyTripleGeneration:
|
|||
# Find comment triples for Recipe class
|
||||
recipe_comment_triples = [
|
||||
t for t in triples
|
||||
if t.s.value == "http://purl.org/ontology/fo/Recipe"
|
||||
and t.p.value == "http://www.w3.org/2000/01/rdf-schema#comment"
|
||||
if t.s.iri == "http://purl.org/ontology/fo/Recipe"
|
||||
and t.p.iri == "http://www.w3.org/2000/01/rdf-schema#comment"
|
||||
]
|
||||
|
||||
assert len(recipe_comment_triples) == 1, "Should generate comment triple for class"
|
||||
|
|
@ -139,13 +139,13 @@ class TestOntologyTripleGeneration:
|
|||
# Find type triples for ingredients property
|
||||
ingredients_type_triples = [
|
||||
t for t in triples
|
||||
if t.s.value == "http://purl.org/ontology/fo/ingredients"
|
||||
and t.p.value == "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"
|
||||
if t.s.iri == "http://purl.org/ontology/fo/ingredients"
|
||||
and t.p.iri == "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"
|
||||
]
|
||||
|
||||
assert len(ingredients_type_triples) == 1, \
|
||||
"Should generate exactly one type triple per object property"
|
||||
assert ingredients_type_triples[0].o.value == "http://www.w3.org/2002/07/owl#ObjectProperty", \
|
||||
assert ingredients_type_triples[0].o.iri == "http://www.w3.org/2002/07/owl#ObjectProperty", \
|
||||
"Object property type should be owl:ObjectProperty"
|
||||
|
||||
def test_generates_object_property_labels(self, extractor, sample_ontology_subset):
|
||||
|
|
@ -155,8 +155,8 @@ class TestOntologyTripleGeneration:
|
|||
# Find label triples for ingredients property
|
||||
ingredients_label_triples = [
|
||||
t for t in triples
|
||||
if t.s.value == "http://purl.org/ontology/fo/ingredients"
|
||||
and t.p.value == "http://www.w3.org/2000/01/rdf-schema#label"
|
||||
if t.s.iri == "http://purl.org/ontology/fo/ingredients"
|
||||
and t.p.iri == "http://www.w3.org/2000/01/rdf-schema#label"
|
||||
]
|
||||
|
||||
assert len(ingredients_label_triples) == 1, \
|
||||
|
|
@ -171,15 +171,15 @@ class TestOntologyTripleGeneration:
|
|||
# Find domain triples for ingredients property
|
||||
ingredients_domain_triples = [
|
||||
t for t in triples
|
||||
if t.s.value == "http://purl.org/ontology/fo/ingredients"
|
||||
and t.p.value == "http://www.w3.org/2000/01/rdf-schema#domain"
|
||||
if t.s.iri == "http://purl.org/ontology/fo/ingredients"
|
||||
and t.p.iri == "http://www.w3.org/2000/01/rdf-schema#domain"
|
||||
]
|
||||
|
||||
assert len(ingredients_domain_triples) == 1, \
|
||||
"Should generate domain triple for object property"
|
||||
assert ingredients_domain_triples[0].o.value == "http://purl.org/ontology/fo/Recipe", \
|
||||
assert ingredients_domain_triples[0].o.iri == "http://purl.org/ontology/fo/Recipe", \
|
||||
"Domain should be Recipe class URI"
|
||||
assert ingredients_domain_triples[0].o.is_uri, \
|
||||
assert ingredients_domain_triples[0].o.type == IRI, \
|
||||
"Domain should be a URI reference"
|
||||
|
||||
def test_generates_object_property_range(self, extractor, sample_ontology_subset):
|
||||
|
|
@ -189,13 +189,13 @@ class TestOntologyTripleGeneration:
|
|||
# Find range triples for produces property
|
||||
produces_range_triples = [
|
||||
t for t in triples
|
||||
if t.s.value == "http://purl.org/ontology/fo/produces"
|
||||
and t.p.value == "http://www.w3.org/2000/01/rdf-schema#range"
|
||||
if t.s.iri == "http://purl.org/ontology/fo/produces"
|
||||
and t.p.iri == "http://www.w3.org/2000/01/rdf-schema#range"
|
||||
]
|
||||
|
||||
assert len(produces_range_triples) == 1, \
|
||||
"Should generate range triple for object property"
|
||||
assert produces_range_triples[0].o.value == "http://purl.org/ontology/fo/Food", \
|
||||
assert produces_range_triples[0].o.iri == "http://purl.org/ontology/fo/Food", \
|
||||
"Range should be Food class URI"
|
||||
|
||||
def test_generates_datatype_property_type_triples(self, extractor, sample_ontology_subset):
|
||||
|
|
@ -205,13 +205,13 @@ class TestOntologyTripleGeneration:
|
|||
# Find type triples for serves property
|
||||
serves_type_triples = [
|
||||
t for t in triples
|
||||
if t.s.value == "http://purl.org/ontology/fo/serves"
|
||||
and t.p.value == "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"
|
||||
if t.s.iri == "http://purl.org/ontology/fo/serves"
|
||||
and t.p.iri == "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"
|
||||
]
|
||||
|
||||
assert len(serves_type_triples) == 1, \
|
||||
"Should generate exactly one type triple per datatype property"
|
||||
assert serves_type_triples[0].o.value == "http://www.w3.org/2002/07/owl#DatatypeProperty", \
|
||||
assert serves_type_triples[0].o.iri == "http://www.w3.org/2002/07/owl#DatatypeProperty", \
|
||||
"Datatype property type should be owl:DatatypeProperty"
|
||||
|
||||
def test_generates_datatype_property_range(self, extractor, sample_ontology_subset):
|
||||
|
|
@ -221,13 +221,13 @@ class TestOntologyTripleGeneration:
|
|||
# Find range triples for serves property
|
||||
serves_range_triples = [
|
||||
t for t in triples
|
||||
if t.s.value == "http://purl.org/ontology/fo/serves"
|
||||
and t.p.value == "http://www.w3.org/2000/01/rdf-schema#range"
|
||||
if t.s.iri == "http://purl.org/ontology/fo/serves"
|
||||
and t.p.iri == "http://www.w3.org/2000/01/rdf-schema#range"
|
||||
]
|
||||
|
||||
assert len(serves_range_triples) == 1, \
|
||||
"Should generate range triple for datatype property"
|
||||
assert serves_range_triples[0].o.value == "http://www.w3.org/2001/XMLSchema#string", \
|
||||
assert serves_range_triples[0].o.iri == "http://www.w3.org/2001/XMLSchema#string", \
|
||||
"Range should be XSD type URI (xsd:string expanded)"
|
||||
|
||||
def test_generates_triples_for_all_classes(self, extractor, sample_ontology_subset):
|
||||
|
|
@ -236,9 +236,9 @@ class TestOntologyTripleGeneration:
|
|||
|
||||
# Count unique class subjects
|
||||
class_subjects = set(
|
||||
t.s.value for t in triples
|
||||
if t.p.value == "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"
|
||||
and t.o.value == "http://www.w3.org/2002/07/owl#Class"
|
||||
t.s.iri for t in triples
|
||||
if t.p.iri == "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"
|
||||
and t.o.iri == "http://www.w3.org/2002/07/owl#Class"
|
||||
)
|
||||
|
||||
assert len(class_subjects) == 3, \
|
||||
|
|
@ -250,9 +250,9 @@ class TestOntologyTripleGeneration:
|
|||
|
||||
# Count unique property subjects (object + datatype properties)
|
||||
property_subjects = set(
|
||||
t.s.value for t in triples
|
||||
if t.p.value == "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"
|
||||
and ("ObjectProperty" in t.o.value or "DatatypeProperty" in t.o.value)
|
||||
t.s.iri for t in triples
|
||||
if t.p.iri == "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"
|
||||
and ("ObjectProperty" in t.o.iri or "DatatypeProperty" in t.o.iri)
|
||||
)
|
||||
|
||||
assert len(property_subjects) == 3, \
|
||||
|
|
@ -276,7 +276,7 @@ class TestOntologyTripleGeneration:
|
|||
# Should still generate proper RDF triples despite dict field names
|
||||
label_triples = [
|
||||
t for t in triples
|
||||
if t.p.value == "http://www.w3.org/2000/01/rdf-schema#label"
|
||||
if t.p.iri == "http://www.w3.org/2000/01/rdf-schema#label"
|
||||
]
|
||||
assert len(label_triples) > 0, \
|
||||
"Should generate rdfs:label triples from dict 'labels' field"
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ and extracts/validates triples from LLM responses.
|
|||
import pytest
|
||||
from trustgraph.extract.kg.ontology.extract import Processor
|
||||
from trustgraph.extract.kg.ontology.ontology_selector import OntologySubset
|
||||
from trustgraph.schema.core.primitives import Triple, Value
|
||||
from trustgraph.schema.core.primitives import Triple, Term, IRI, LITERAL
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
|
@ -248,9 +248,9 @@ class TestTripleParsing:
|
|||
validated = extractor.parse_and_validate_triples(triples_response, sample_ontology_subset)
|
||||
|
||||
assert len(validated) == 1, "Should parse one valid triple"
|
||||
assert validated[0].s.value == "https://trustgraph.ai/food/cornish-pasty"
|
||||
assert validated[0].p.value == "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"
|
||||
assert validated[0].o.value == "http://purl.org/ontology/fo/Recipe"
|
||||
assert validated[0].s.iri == "https://trustgraph.ai/food/cornish-pasty"
|
||||
assert validated[0].p.iri == "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"
|
||||
assert validated[0].o.iri == "http://purl.org/ontology/fo/Recipe"
|
||||
|
||||
def test_parse_multiple_triples(self, extractor, sample_ontology_subset):
|
||||
"""Test parsing multiple triples."""
|
||||
|
|
@ -307,11 +307,11 @@ class TestTripleParsing:
|
|||
|
||||
assert len(validated) == 1
|
||||
# Subject should be expanded to entity URI
|
||||
assert validated[0].s.value.startswith("https://trustgraph.ai/food/")
|
||||
assert validated[0].s.iri.startswith("https://trustgraph.ai/food/")
|
||||
# Predicate should be expanded to ontology URI
|
||||
assert validated[0].p.value == "http://purl.org/ontology/fo/produces"
|
||||
assert validated[0].p.iri == "http://purl.org/ontology/fo/produces"
|
||||
# Object should be expanded to class URI
|
||||
assert validated[0].o.value == "http://purl.org/ontology/fo/Food"
|
||||
assert validated[0].o.iri == "http://purl.org/ontology/fo/Food"
|
||||
|
||||
def test_creates_proper_triple_objects(self, extractor, sample_ontology_subset):
|
||||
"""Test that Triple objects are properly created."""
|
||||
|
|
@ -324,12 +324,12 @@ class TestTripleParsing:
|
|||
assert len(validated) == 1
|
||||
triple = validated[0]
|
||||
assert isinstance(triple, Triple), "Should create Triple objects"
|
||||
assert isinstance(triple.s, Value), "Subject should be Value object"
|
||||
assert isinstance(triple.p, Value), "Predicate should be Value object"
|
||||
assert isinstance(triple.o, Value), "Object should be Value object"
|
||||
assert triple.s.is_uri, "Subject should be marked as URI"
|
||||
assert triple.p.is_uri, "Predicate should be marked as URI"
|
||||
assert not triple.o.is_uri, "Object literal should not be marked as URI"
|
||||
assert isinstance(triple.s, Term), "Subject should be Term object"
|
||||
assert isinstance(triple.p, Term), "Predicate should be Term object"
|
||||
assert isinstance(triple.o, Term), "Object should be Term object"
|
||||
assert triple.s.type == IRI, "Subject should be IRI type"
|
||||
assert triple.p.type == IRI, "Predicate should be IRI type"
|
||||
assert triple.o.type == LITERAL, "Object literal should be LITERAL type"
|
||||
|
||||
|
||||
class TestURIExpansionInExtraction:
|
||||
|
|
@ -343,8 +343,8 @@ class TestURIExpansionInExtraction:
|
|||
|
||||
validated = extractor.parse_and_validate_triples(triples_response, sample_ontology_subset)
|
||||
|
||||
assert validated[0].o.value == "http://purl.org/ontology/fo/Recipe"
|
||||
assert validated[0].o.is_uri, "Class reference should be URI"
|
||||
assert validated[0].o.iri == "http://purl.org/ontology/fo/Recipe"
|
||||
assert validated[0].o.type == IRI, "Class reference should be URI"
|
||||
|
||||
def test_expands_property_names(self, extractor, sample_ontology_subset):
|
||||
"""Test that property names are expanded to full URIs."""
|
||||
|
|
@ -354,7 +354,7 @@ class TestURIExpansionInExtraction:
|
|||
|
||||
validated = extractor.parse_and_validate_triples(triples_response, sample_ontology_subset)
|
||||
|
||||
assert validated[0].p.value == "http://purl.org/ontology/fo/produces"
|
||||
assert validated[0].p.iri == "http://purl.org/ontology/fo/produces"
|
||||
|
||||
def test_expands_entity_instances(self, extractor, sample_ontology_subset):
|
||||
"""Test that entity instances get constructed URIs."""
|
||||
|
|
@ -364,8 +364,8 @@ class TestURIExpansionInExtraction:
|
|||
|
||||
validated = extractor.parse_and_validate_triples(triples_response, sample_ontology_subset)
|
||||
|
||||
assert validated[0].s.value.startswith("https://trustgraph.ai/food/")
|
||||
assert "my-special-recipe" in validated[0].s.value
|
||||
assert validated[0].s.iri.startswith("https://trustgraph.ai/food/")
|
||||
assert "my-special-recipe" in validated[0].s.iri
|
||||
|
||||
|
||||
class TestEdgeCases:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue