mirror of
https://github.com/trustgraph-ai/trustgraph.git
synced 2026-04-27 01:16:22 +02:00
Changed schema for Value -> Term, majorly breaking change (#622)
* Changed schema for Value -> Term, majorly breaking change * Following the schema change, Value -> Term into all processing * Updated Cassandra for g, p, s, o index patterns (7 indexes) * Reviewed and updated all tests * Neo4j, Memgraph and FalkorDB remain broken, will look at once settled down
This commit is contained in:
parent
e061f2c633
commit
cf0daedefa
86 changed files with 2458 additions and 1764 deletions
|
|
@ -11,7 +11,7 @@ from unittest.mock import AsyncMock, Mock, patch, MagicMock
|
|||
from unittest.mock import call
|
||||
|
||||
from trustgraph.cores.knowledge import KnowledgeManager
|
||||
from trustgraph.schema import KnowledgeResponse, Triples, GraphEmbeddings, Metadata, Triple, Value, EntityEmbeddings
|
||||
from trustgraph.schema import KnowledgeResponse, Triples, GraphEmbeddings, Metadata, Triple, Term, EntityEmbeddings, IRI, LITERAL
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
|
@ -71,15 +71,15 @@ def sample_triples():
|
|||
return Triples(
|
||||
metadata=Metadata(
|
||||
id="test-doc-id",
|
||||
user="test-user",
|
||||
user="test-user",
|
||||
collection="default", # This should be overridden
|
||||
metadata=[]
|
||||
),
|
||||
triples=[
|
||||
Triple(
|
||||
s=Value(value="http://example.org/john", is_uri=True),
|
||||
p=Value(value="http://example.org/name", is_uri=True),
|
||||
o=Value(value="John Smith", is_uri=False)
|
||||
s=Term(type=IRI, iri="http://example.org/john"),
|
||||
p=Term(type=IRI, iri="http://example.org/name"),
|
||||
o=Term(type=LITERAL, value="John Smith")
|
||||
)
|
||||
]
|
||||
)
|
||||
|
|
@ -97,7 +97,7 @@ def sample_graph_embeddings():
|
|||
),
|
||||
entities=[
|
||||
EntityEmbeddings(
|
||||
entity=Value(value="http://example.org/john", is_uri=True),
|
||||
entity=Term(type=IRI, iri="http://example.org/john"),
|
||||
vectors=[[0.1, 0.2, 0.3]]
|
||||
)
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import pytest
|
|||
from unittest.mock import MagicMock
|
||||
|
||||
from trustgraph.gateway.dispatch.serialize import to_value, to_subgraph, serialize_value
|
||||
from trustgraph.schema import Value, Triple
|
||||
from trustgraph.schema import Term, Triple, IRI, LITERAL
|
||||
|
||||
|
||||
class TestDispatchSerialize:
|
||||
|
|
@ -14,55 +14,55 @@ class TestDispatchSerialize:
|
|||
|
||||
def test_to_value_with_uri(self):
|
||||
"""Test to_value function with URI"""
|
||||
input_data = {"v": "http://example.com/resource", "e": True}
|
||||
|
||||
input_data = {"t": "i", "i": "http://example.com/resource"}
|
||||
|
||||
result = to_value(input_data)
|
||||
|
||||
assert isinstance(result, Value)
|
||||
assert result.value == "http://example.com/resource"
|
||||
assert result.is_uri is True
|
||||
|
||||
assert isinstance(result, Term)
|
||||
assert result.iri == "http://example.com/resource"
|
||||
assert result.type == IRI
|
||||
|
||||
def test_to_value_with_literal(self):
|
||||
"""Test to_value function with literal value"""
|
||||
input_data = {"v": "literal string", "e": False}
|
||||
|
||||
input_data = {"t": "l", "v": "literal string"}
|
||||
|
||||
result = to_value(input_data)
|
||||
|
||||
assert isinstance(result, Value)
|
||||
|
||||
assert isinstance(result, Term)
|
||||
assert result.value == "literal string"
|
||||
assert result.is_uri is False
|
||||
assert result.type == LITERAL
|
||||
|
||||
def test_to_subgraph_with_multiple_triples(self):
|
||||
"""Test to_subgraph function with multiple triples"""
|
||||
input_data = [
|
||||
{
|
||||
"s": {"v": "subject1", "e": True},
|
||||
"p": {"v": "predicate1", "e": True},
|
||||
"o": {"v": "object1", "e": False}
|
||||
"s": {"t": "i", "i": "subject1"},
|
||||
"p": {"t": "i", "i": "predicate1"},
|
||||
"o": {"t": "l", "v": "object1"}
|
||||
},
|
||||
{
|
||||
"s": {"v": "subject2", "e": False},
|
||||
"p": {"v": "predicate2", "e": True},
|
||||
"o": {"v": "object2", "e": True}
|
||||
"s": {"t": "l", "v": "subject2"},
|
||||
"p": {"t": "i", "i": "predicate2"},
|
||||
"o": {"t": "i", "i": "object2"}
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
result = to_subgraph(input_data)
|
||||
|
||||
|
||||
assert len(result) == 2
|
||||
assert all(isinstance(triple, Triple) for triple in result)
|
||||
|
||||
|
||||
# Check first triple
|
||||
assert result[0].s.value == "subject1"
|
||||
assert result[0].s.is_uri is True
|
||||
assert result[0].p.value == "predicate1"
|
||||
assert result[0].p.is_uri is True
|
||||
assert result[0].s.iri == "subject1"
|
||||
assert result[0].s.type == IRI
|
||||
assert result[0].p.iri == "predicate1"
|
||||
assert result[0].p.type == IRI
|
||||
assert result[0].o.value == "object1"
|
||||
assert result[0].o.is_uri is False
|
||||
|
||||
assert result[0].o.type == LITERAL
|
||||
|
||||
# Check second triple
|
||||
assert result[1].s.value == "subject2"
|
||||
assert result[1].s.is_uri is False
|
||||
assert result[1].s.type == LITERAL
|
||||
|
||||
def test_to_subgraph_with_empty_list(self):
|
||||
"""Test to_subgraph function with empty input"""
|
||||
|
|
@ -74,16 +74,16 @@ class TestDispatchSerialize:
|
|||
|
||||
def test_serialize_value_with_uri(self):
|
||||
"""Test serialize_value function with URI value"""
|
||||
value = Value(value="http://example.com/test", is_uri=True)
|
||||
|
||||
result = serialize_value(value)
|
||||
|
||||
assert result == {"v": "http://example.com/test", "e": True}
|
||||
term = Term(type=IRI, iri="http://example.com/test")
|
||||
|
||||
result = serialize_value(term)
|
||||
|
||||
assert result == {"t": "i", "i": "http://example.com/test"}
|
||||
|
||||
def test_serialize_value_with_literal(self):
|
||||
"""Test serialize_value function with literal value"""
|
||||
value = Value(value="test literal", is_uri=False)
|
||||
|
||||
result = serialize_value(value)
|
||||
|
||||
assert result == {"v": "test literal", "e": False}
|
||||
term = Term(type=LITERAL, value="test literal")
|
||||
|
||||
result = serialize_value(term)
|
||||
|
||||
assert result == {"t": "l", "v": "test literal"}
|
||||
|
|
@ -6,11 +6,21 @@ import pytest
|
|||
from unittest.mock import Mock, AsyncMock
|
||||
|
||||
# Mock schema classes for testing
|
||||
class Value:
|
||||
def __init__(self, value, is_uri, type):
|
||||
self.value = value
|
||||
self.is_uri = is_uri
|
||||
# Term type constants
|
||||
IRI = "i"
|
||||
LITERAL = "l"
|
||||
BLANK = "b"
|
||||
TRIPLE = "t"
|
||||
|
||||
class Term:
|
||||
def __init__(self, type, iri=None, value=None, id=None, datatype=None, language=None, triple=None):
|
||||
self.type = type
|
||||
self.iri = iri
|
||||
self.value = value
|
||||
self.id = id
|
||||
self.datatype = datatype
|
||||
self.language = language
|
||||
self.triple = triple
|
||||
|
||||
class Triple:
|
||||
def __init__(self, s, p, o):
|
||||
|
|
@ -66,32 +76,30 @@ def sample_relationships():
|
|||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_value_uri():
|
||||
"""Sample URI Value object"""
|
||||
return Value(
|
||||
value="http://example.com/person/john-smith",
|
||||
is_uri=True,
|
||||
type=""
|
||||
def sample_term_uri():
|
||||
"""Sample URI Term object"""
|
||||
return Term(
|
||||
type=IRI,
|
||||
iri="http://example.com/person/john-smith"
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_value_literal():
|
||||
"""Sample literal Value object"""
|
||||
return Value(
|
||||
value="John Smith",
|
||||
is_uri=False,
|
||||
type="string"
|
||||
def sample_term_literal():
|
||||
"""Sample literal Term object"""
|
||||
return Term(
|
||||
type=LITERAL,
|
||||
value="John Smith"
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_triple(sample_value_uri, sample_value_literal):
|
||||
def sample_triple(sample_term_uri, sample_term_literal):
|
||||
"""Sample Triple object"""
|
||||
return Triple(
|
||||
s=sample_value_uri,
|
||||
p=Value(value="http://schema.org/name", is_uri=True, type=""),
|
||||
o=sample_value_literal
|
||||
s=sample_term_uri,
|
||||
p=Term(type=IRI, iri="http://schema.org/name"),
|
||||
o=sample_term_literal
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import json
|
|||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
from trustgraph.extract.kg.agent.extract import Processor as AgentKgExtractor
|
||||
from trustgraph.schema import Chunk, Triple, Triples, Metadata, Value, Error
|
||||
from trustgraph.schema import Chunk, Triple, Triples, Metadata, Term, Error, IRI, LITERAL
|
||||
from trustgraph.schema import EntityContext, EntityContexts
|
||||
from trustgraph.rdf import TRUSTGRAPH_ENTITIES, DEFINITION, RDF_LABEL, SUBJECT_OF
|
||||
from trustgraph.template.prompt_manager import PromptManager
|
||||
|
|
@ -53,9 +53,9 @@ class TestAgentKgExtractor:
|
|||
id="doc123",
|
||||
metadata=[
|
||||
Triple(
|
||||
s=Value(value="doc123", is_uri=True),
|
||||
p=Value(value="http://example.org/type", is_uri=True),
|
||||
o=Value(value="document", is_uri=False)
|
||||
s=Term(type=IRI, iri="doc123"),
|
||||
p=Term(type=IRI, iri="http://example.org/type"),
|
||||
o=Term(type=LITERAL, value="document")
|
||||
)
|
||||
]
|
||||
)
|
||||
|
|
@ -178,27 +178,27 @@ This is not JSON at all
|
|||
triples, entity_contexts = agent_extractor.process_extraction_data(data, sample_metadata)
|
||||
|
||||
# Check entity label triple
|
||||
label_triple = next((t for t in triples if t.p.value == RDF_LABEL and t.o.value == "Machine Learning"), None)
|
||||
label_triple = next((t for t in triples if t.p.iri == RDF_LABEL and t.o.value == "Machine Learning"), None)
|
||||
assert label_triple is not None
|
||||
assert label_triple.s.value == f"{TRUSTGRAPH_ENTITIES}Machine%20Learning"
|
||||
assert label_triple.s.is_uri == True
|
||||
assert label_triple.o.is_uri == False
|
||||
|
||||
assert label_triple.s.iri == f"{TRUSTGRAPH_ENTITIES}Machine%20Learning"
|
||||
assert label_triple.s.type == IRI
|
||||
assert label_triple.o.type == LITERAL
|
||||
|
||||
# Check definition triple
|
||||
def_triple = next((t for t in triples if t.p.value == DEFINITION), None)
|
||||
def_triple = next((t for t in triples if t.p.iri == DEFINITION), None)
|
||||
assert def_triple is not None
|
||||
assert def_triple.s.value == f"{TRUSTGRAPH_ENTITIES}Machine%20Learning"
|
||||
assert def_triple.s.iri == f"{TRUSTGRAPH_ENTITIES}Machine%20Learning"
|
||||
assert def_triple.o.value == "A subset of AI that enables learning from data."
|
||||
|
||||
|
||||
# Check subject-of triple
|
||||
subject_of_triple = next((t for t in triples if t.p.value == SUBJECT_OF), None)
|
||||
subject_of_triple = next((t for t in triples if t.p.iri == SUBJECT_OF), None)
|
||||
assert subject_of_triple is not None
|
||||
assert subject_of_triple.s.value == f"{TRUSTGRAPH_ENTITIES}Machine%20Learning"
|
||||
assert subject_of_triple.o.value == "doc123"
|
||||
|
||||
assert subject_of_triple.s.iri == f"{TRUSTGRAPH_ENTITIES}Machine%20Learning"
|
||||
assert subject_of_triple.o.iri == "doc123"
|
||||
|
||||
# Check entity context
|
||||
assert len(entity_contexts) == 1
|
||||
assert entity_contexts[0].entity.value == f"{TRUSTGRAPH_ENTITIES}Machine%20Learning"
|
||||
assert entity_contexts[0].entity.iri == f"{TRUSTGRAPH_ENTITIES}Machine%20Learning"
|
||||
assert entity_contexts[0].context == "A subset of AI that enables learning from data."
|
||||
|
||||
def test_process_extraction_data_relationships(self, agent_extractor, sample_metadata):
|
||||
|
|
@ -218,25 +218,25 @@ This is not JSON at all
|
|||
# Check that subject, predicate, and object labels are created
|
||||
subject_uri = f"{TRUSTGRAPH_ENTITIES}Machine%20Learning"
|
||||
predicate_uri = f"{TRUSTGRAPH_ENTITIES}is_subset_of"
|
||||
|
||||
|
||||
# Find label triples
|
||||
subject_label = next((t for t in triples if t.s.value == subject_uri and t.p.value == RDF_LABEL), None)
|
||||
subject_label = next((t for t in triples if t.s.iri == subject_uri and t.p.iri == RDF_LABEL), None)
|
||||
assert subject_label is not None
|
||||
assert subject_label.o.value == "Machine Learning"
|
||||
|
||||
predicate_label = next((t for t in triples if t.s.value == predicate_uri and t.p.value == RDF_LABEL), None)
|
||||
|
||||
predicate_label = next((t for t in triples if t.s.iri == predicate_uri and t.p.iri == RDF_LABEL), None)
|
||||
assert predicate_label is not None
|
||||
assert predicate_label.o.value == "is_subset_of"
|
||||
|
||||
|
||||
# Check main relationship triple
|
||||
object_uri = f"{TRUSTGRAPH_ENTITIES}Artificial%20Intelligence"
|
||||
rel_triple = next((t for t in triples if t.s.value == subject_uri and t.p.value == predicate_uri), None)
|
||||
rel_triple = next((t for t in triples if t.s.iri == subject_uri and t.p.iri == predicate_uri), None)
|
||||
assert rel_triple is not None
|
||||
assert rel_triple.o.value == object_uri
|
||||
assert rel_triple.o.is_uri == True
|
||||
|
||||
assert rel_triple.o.iri == object_uri
|
||||
assert rel_triple.o.type == IRI
|
||||
|
||||
# Check subject-of relationships
|
||||
subject_of_triples = [t for t in triples if t.p.value == SUBJECT_OF and t.o.value == "doc123"]
|
||||
subject_of_triples = [t for t in triples if t.p.iri == SUBJECT_OF and t.o.iri == "doc123"]
|
||||
assert len(subject_of_triples) >= 2 # At least subject and predicate should have subject-of relations
|
||||
|
||||
def test_process_extraction_data_literal_object(self, agent_extractor, sample_metadata):
|
||||
|
|
@ -254,7 +254,7 @@ This is not JSON at all
|
|||
triples, entity_contexts = agent_extractor.process_extraction_data(data, sample_metadata)
|
||||
|
||||
# Check that object labels are not created for literal objects
|
||||
object_labels = [t for t in triples if t.p.value == RDF_LABEL and t.o.value == "95%"]
|
||||
object_labels = [t for t in triples if t.p.iri == RDF_LABEL and t.o.value == "95%"]
|
||||
# Based on the code logic, it should not create object labels for non-entity objects
|
||||
# But there might be a bug in the original implementation
|
||||
|
||||
|
|
@ -263,12 +263,12 @@ This is not JSON at all
|
|||
triples, entity_contexts = agent_extractor.process_extraction_data(sample_extraction_data, sample_metadata)
|
||||
|
||||
# Check that we have both definition and relationship triples
|
||||
definition_triples = [t for t in triples if t.p.value == DEFINITION]
|
||||
definition_triples = [t for t in triples if t.p.iri == DEFINITION]
|
||||
assert len(definition_triples) == 2 # Two definitions
|
||||
|
||||
|
||||
# Check entity contexts are created for definitions
|
||||
assert len(entity_contexts) == 2
|
||||
entity_uris = [ec.entity.value for ec in entity_contexts]
|
||||
entity_uris = [ec.entity.iri for ec in entity_contexts]
|
||||
assert f"{TRUSTGRAPH_ENTITIES}Machine%20Learning" in entity_uris
|
||||
assert f"{TRUSTGRAPH_ENTITIES}Neural%20Networks" in entity_uris
|
||||
|
||||
|
|
@ -282,7 +282,7 @@ This is not JSON at all
|
|||
triples, entity_contexts = agent_extractor.process_extraction_data(data, metadata)
|
||||
|
||||
# Should not create subject-of relationships when no metadata ID
|
||||
subject_of_triples = [t for t in triples if t.p.value == SUBJECT_OF]
|
||||
subject_of_triples = [t for t in triples if t.p.iri == SUBJECT_OF]
|
||||
assert len(subject_of_triples) == 0
|
||||
|
||||
# Should still create entity contexts
|
||||
|
|
@ -327,17 +327,17 @@ This is not JSON at all
|
|||
async def test_emit_triples(self, agent_extractor, sample_metadata):
|
||||
"""Test emitting triples to publisher"""
|
||||
mock_publisher = AsyncMock()
|
||||
|
||||
|
||||
test_triples = [
|
||||
Triple(
|
||||
s=Value(value="test:subject", is_uri=True),
|
||||
p=Value(value="test:predicate", is_uri=True),
|
||||
o=Value(value="test object", is_uri=False)
|
||||
s=Term(type=IRI, iri="test:subject"),
|
||||
p=Term(type=IRI, iri="test:predicate"),
|
||||
o=Term(type=LITERAL, value="test object")
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
await agent_extractor.emit_triples(mock_publisher, sample_metadata, test_triples)
|
||||
|
||||
|
||||
mock_publisher.send.assert_called_once()
|
||||
sent_triples = mock_publisher.send.call_args[0][0]
|
||||
assert isinstance(sent_triples, Triples)
|
||||
|
|
@ -348,22 +348,22 @@ This is not JSON at all
|
|||
# Note: metadata.metadata is now empty array in the new implementation
|
||||
assert sent_triples.metadata.metadata == []
|
||||
assert len(sent_triples.triples) == 1
|
||||
assert sent_triples.triples[0].s.value == "test:subject"
|
||||
assert sent_triples.triples[0].s.iri == "test:subject"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_emit_entity_contexts(self, agent_extractor, sample_metadata):
|
||||
"""Test emitting entity contexts to publisher"""
|
||||
mock_publisher = AsyncMock()
|
||||
|
||||
|
||||
test_contexts = [
|
||||
EntityContext(
|
||||
entity=Value(value="test:entity", is_uri=True),
|
||||
entity=Term(type=IRI, iri="test:entity"),
|
||||
context="Test context"
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
await agent_extractor.emit_entity_contexts(mock_publisher, sample_metadata, test_contexts)
|
||||
|
||||
|
||||
mock_publisher.send.assert_called_once()
|
||||
sent_contexts = mock_publisher.send.call_args[0][0]
|
||||
assert isinstance(sent_contexts, EntityContexts)
|
||||
|
|
@ -374,7 +374,7 @@ This is not JSON at all
|
|||
# Note: metadata.metadata is now empty array in the new implementation
|
||||
assert sent_contexts.metadata.metadata == []
|
||||
assert len(sent_contexts.entities) == 1
|
||||
assert sent_contexts.entities[0].entity.value == "test:entity"
|
||||
assert sent_contexts.entities[0].entity.iri == "test:entity"
|
||||
|
||||
def test_agent_extractor_initialization_params(self):
|
||||
"""Test agent extractor parameter validation"""
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import urllib.parse
|
|||
from unittest.mock import AsyncMock, MagicMock
|
||||
|
||||
from trustgraph.extract.kg.agent.extract import Processor as AgentKgExtractor
|
||||
from trustgraph.schema import Chunk, Triple, Triples, Metadata, Value
|
||||
from trustgraph.schema import Chunk, Triple, Triples, Metadata, Term, IRI, LITERAL
|
||||
from trustgraph.schema import EntityContext, EntityContexts
|
||||
from trustgraph.rdf import TRUSTGRAPH_ENTITIES, DEFINITION, RDF_LABEL, SUBJECT_OF
|
||||
|
||||
|
|
@ -188,7 +188,7 @@ class TestAgentKgExtractionEdgeCases:
|
|||
triples, contexts = agent_extractor.process_extraction_data(data, metadata)
|
||||
|
||||
# Should not create subject-of triples when ID is empty string
|
||||
subject_of_triples = [t for t in triples if t.p.value == SUBJECT_OF]
|
||||
subject_of_triples = [t for t in triples if t.p.iri == SUBJECT_OF]
|
||||
assert len(subject_of_triples) == 0
|
||||
|
||||
def test_process_extraction_data_special_entity_names(self, agent_extractor):
|
||||
|
|
@ -221,7 +221,7 @@ class TestAgentKgExtractionEdgeCases:
|
|||
# Verify URIs were properly encoded
|
||||
for i, entity in enumerate(special_entities):
|
||||
expected_uri = f"{TRUSTGRAPH_ENTITIES}{urllib.parse.quote(entity)}"
|
||||
assert contexts[i].entity.value == expected_uri
|
||||
assert contexts[i].entity.iri == expected_uri
|
||||
|
||||
def test_process_extraction_data_very_long_definitions(self, agent_extractor):
|
||||
"""Test processing with very long entity definitions"""
|
||||
|
|
@ -241,7 +241,7 @@ class TestAgentKgExtractionEdgeCases:
|
|||
assert contexts[0].context == long_definition
|
||||
|
||||
# Find definition triple
|
||||
def_triple = next((t for t in triples if t.p.value == DEFINITION), None)
|
||||
def_triple = next((t for t in triples if t.p.iri == DEFINITION), None)
|
||||
assert def_triple is not None
|
||||
assert def_triple.o.value == long_definition
|
||||
|
||||
|
|
@ -262,7 +262,7 @@ class TestAgentKgExtractionEdgeCases:
|
|||
assert len(contexts) == 4
|
||||
|
||||
# Check that both definitions for "Machine Learning" are present
|
||||
ml_contexts = [ec for ec in contexts if "Machine%20Learning" in ec.entity.value]
|
||||
ml_contexts = [ec for ec in contexts if "Machine%20Learning" in ec.entity.iri]
|
||||
assert len(ml_contexts) == 2
|
||||
assert ml_contexts[0].context == "First definition"
|
||||
assert ml_contexts[1].context == "Second definition"
|
||||
|
|
@ -286,7 +286,7 @@ class TestAgentKgExtractionEdgeCases:
|
|||
assert len(contexts) == 3
|
||||
|
||||
# Empty entity should create empty URI after encoding
|
||||
empty_entity_context = next((ec for ec in contexts if ec.entity.value == TRUSTGRAPH_ENTITIES), None)
|
||||
empty_entity_context = next((ec for ec in contexts if ec.entity.iri == TRUSTGRAPH_ENTITIES), None)
|
||||
assert empty_entity_context is not None
|
||||
|
||||
def test_process_extraction_data_nested_json_in_strings(self, agent_extractor):
|
||||
|
|
@ -338,7 +338,7 @@ class TestAgentKgExtractionEdgeCases:
|
|||
|
||||
# Should process all relationships
|
||||
# Note: The current implementation has some logic issues that these tests document
|
||||
assert len([t for t in triples if t.p.value != RDF_LABEL and t.p.value != SUBJECT_OF]) >= 7
|
||||
assert len([t for t in triples if t.p.iri != RDF_LABEL and t.p.iri != SUBJECT_OF]) >= 7
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_emit_empty_collections(self, agent_extractor):
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ processing graph structures, and performing graph operations.
|
|||
|
||||
import pytest
|
||||
from unittest.mock import Mock
|
||||
from .conftest import Triple, Value, Metadata
|
||||
from .conftest import Triple, Metadata
|
||||
from collections import defaultdict, deque
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@
|
|||
Unit tests for triple construction logic
|
||||
|
||||
Tests the core business logic for constructing RDF triples from extracted
|
||||
entities and relationships, including URI generation, Value object creation,
|
||||
entities and relationships, including URI generation, Term object creation,
|
||||
and triple validation.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from unittest.mock import Mock
|
||||
from .conftest import Triple, Triples, Value, Metadata
|
||||
from .conftest import Triple, Triples, Term, Metadata, IRI, LITERAL
|
||||
import re
|
||||
import hashlib
|
||||
|
||||
|
|
@ -48,80 +48,82 @@ class TestTripleConstructionLogic:
|
|||
generated_uri = generate_uri(text, entity_type)
|
||||
assert generated_uri == expected_uri, f"URI generation failed for '{text}'"
|
||||
|
||||
def test_value_object_creation(self):
|
||||
"""Test creation of Value objects for subjects, predicates, and objects"""
|
||||
def test_term_object_creation(self):
|
||||
"""Test creation of Term objects for subjects, predicates, and objects"""
|
||||
# Arrange
|
||||
def create_value_object(text, is_uri, value_type=""):
|
||||
return Value(
|
||||
value=text,
|
||||
is_uri=is_uri,
|
||||
type=value_type
|
||||
)
|
||||
|
||||
def create_term_object(text, is_uri, datatype=""):
|
||||
if is_uri:
|
||||
return Term(type=IRI, iri=text)
|
||||
else:
|
||||
return Term(type=LITERAL, value=text, datatype=datatype if datatype else None)
|
||||
|
||||
test_cases = [
|
||||
("http://trustgraph.ai/kg/person/john-smith", True, ""),
|
||||
("John Smith", False, "string"),
|
||||
("42", False, "integer"),
|
||||
("http://schema.org/worksFor", True, "")
|
||||
]
|
||||
|
||||
|
||||
# Act & Assert
|
||||
for value_text, is_uri, value_type in test_cases:
|
||||
value_obj = create_value_object(value_text, is_uri, value_type)
|
||||
|
||||
assert isinstance(value_obj, Value)
|
||||
assert value_obj.value == value_text
|
||||
assert value_obj.is_uri == is_uri
|
||||
assert value_obj.type == value_type
|
||||
for value_text, is_uri, datatype in test_cases:
|
||||
term_obj = create_term_object(value_text, is_uri, datatype)
|
||||
|
||||
assert isinstance(term_obj, Term)
|
||||
if is_uri:
|
||||
assert term_obj.type == IRI
|
||||
assert term_obj.iri == value_text
|
||||
else:
|
||||
assert term_obj.type == LITERAL
|
||||
assert term_obj.value == value_text
|
||||
|
||||
def test_triple_construction_from_relationship(self):
|
||||
"""Test constructing Triple objects from relationships"""
|
||||
# Arrange
|
||||
relationship = {
|
||||
"subject": "John Smith",
|
||||
"predicate": "works_for",
|
||||
"predicate": "works_for",
|
||||
"object": "OpenAI",
|
||||
"subject_type": "PERSON",
|
||||
"object_type": "ORG"
|
||||
}
|
||||
|
||||
|
||||
def construct_triple(relationship, uri_base="http://trustgraph.ai/kg"):
|
||||
# Generate URIs
|
||||
subject_uri = f"{uri_base}/person/{relationship['subject'].lower().replace(' ', '-')}"
|
||||
object_uri = f"{uri_base}/org/{relationship['object'].lower().replace(' ', '-')}"
|
||||
|
||||
|
||||
# Map predicate to schema.org URI
|
||||
predicate_mappings = {
|
||||
"works_for": "http://schema.org/worksFor",
|
||||
"located_in": "http://schema.org/location",
|
||||
"developed": "http://schema.org/creator"
|
||||
}
|
||||
predicate_uri = predicate_mappings.get(relationship["predicate"],
|
||||
predicate_uri = predicate_mappings.get(relationship["predicate"],
|
||||
f"{uri_base}/predicate/{relationship['predicate']}")
|
||||
|
||||
# Create Value objects
|
||||
subject_value = Value(value=subject_uri, is_uri=True, type="")
|
||||
predicate_value = Value(value=predicate_uri, is_uri=True, type="")
|
||||
object_value = Value(value=object_uri, is_uri=True, type="")
|
||||
|
||||
|
||||
# Create Term objects
|
||||
subject_term = Term(type=IRI, iri=subject_uri)
|
||||
predicate_term = Term(type=IRI, iri=predicate_uri)
|
||||
object_term = Term(type=IRI, iri=object_uri)
|
||||
|
||||
# Create Triple
|
||||
return Triple(
|
||||
s=subject_value,
|
||||
p=predicate_value,
|
||||
o=object_value
|
||||
s=subject_term,
|
||||
p=predicate_term,
|
||||
o=object_term
|
||||
)
|
||||
|
||||
|
||||
# Act
|
||||
triple = construct_triple(relationship)
|
||||
|
||||
|
||||
# Assert
|
||||
assert isinstance(triple, Triple)
|
||||
assert triple.s.value == "http://trustgraph.ai/kg/person/john-smith"
|
||||
assert triple.s.is_uri is True
|
||||
assert triple.p.value == "http://schema.org/worksFor"
|
||||
assert triple.p.is_uri is True
|
||||
assert triple.o.value == "http://trustgraph.ai/kg/org/openai"
|
||||
assert triple.o.is_uri is True
|
||||
assert triple.s.iri == "http://trustgraph.ai/kg/person/john-smith"
|
||||
assert triple.s.type == IRI
|
||||
assert triple.p.iri == "http://schema.org/worksFor"
|
||||
assert triple.p.type == IRI
|
||||
assert triple.o.iri == "http://trustgraph.ai/kg/org/openai"
|
||||
assert triple.o.type == IRI
|
||||
|
||||
def test_literal_value_handling(self):
|
||||
"""Test handling of literal values vs URI values"""
|
||||
|
|
@ -132,10 +134,10 @@ class TestTripleConstructionLogic:
|
|||
("John Smith", "email", "john@example.com", False), # Literal email
|
||||
("John Smith", "worksFor", "http://trustgraph.ai/kg/org/openai", True) # URI reference
|
||||
]
|
||||
|
||||
|
||||
def create_triple_with_literal(subject_uri, predicate, object_value, object_is_uri):
|
||||
subject_val = Value(value=subject_uri, is_uri=True, type="")
|
||||
|
||||
subject_term = Term(type=IRI, iri=subject_uri)
|
||||
|
||||
# Determine predicate URI
|
||||
predicate_mappings = {
|
||||
"name": "http://schema.org/name",
|
||||
|
|
@ -144,32 +146,37 @@ class TestTripleConstructionLogic:
|
|||
"worksFor": "http://schema.org/worksFor"
|
||||
}
|
||||
predicate_uri = predicate_mappings.get(predicate, f"http://trustgraph.ai/kg/predicate/{predicate}")
|
||||
predicate_val = Value(value=predicate_uri, is_uri=True, type="")
|
||||
|
||||
# Create object value with appropriate type
|
||||
object_type = ""
|
||||
if not object_is_uri:
|
||||
predicate_term = Term(type=IRI, iri=predicate_uri)
|
||||
|
||||
# Create object term with appropriate type
|
||||
if object_is_uri:
|
||||
object_term = Term(type=IRI, iri=object_value)
|
||||
else:
|
||||
datatype = None
|
||||
if predicate == "age":
|
||||
object_type = "integer"
|
||||
datatype = "integer"
|
||||
elif predicate in ["name", "email"]:
|
||||
object_type = "string"
|
||||
|
||||
object_val = Value(value=object_value, is_uri=object_is_uri, type=object_type)
|
||||
|
||||
return Triple(s=subject_val, p=predicate_val, o=object_val)
|
||||
|
||||
datatype = "string"
|
||||
object_term = Term(type=LITERAL, value=object_value, datatype=datatype)
|
||||
|
||||
return Triple(s=subject_term, p=predicate_term, o=object_term)
|
||||
|
||||
# Act & Assert
|
||||
for subject_uri, predicate, object_value, object_is_uri in test_data:
|
||||
subject_full_uri = "http://trustgraph.ai/kg/person/john-smith"
|
||||
triple = create_triple_with_literal(subject_full_uri, predicate, object_value, object_is_uri)
|
||||
|
||||
assert triple.o.is_uri == object_is_uri
|
||||
assert triple.o.value == object_value
|
||||
|
||||
|
||||
if object_is_uri:
|
||||
assert triple.o.type == IRI
|
||||
assert triple.o.iri == object_value
|
||||
else:
|
||||
assert triple.o.type == LITERAL
|
||||
assert triple.o.value == object_value
|
||||
|
||||
if predicate == "age":
|
||||
assert triple.o.type == "integer"
|
||||
assert triple.o.datatype == "integer"
|
||||
elif predicate in ["name", "email"]:
|
||||
assert triple.o.type == "string"
|
||||
assert triple.o.datatype == "string"
|
||||
|
||||
def test_namespace_management(self):
|
||||
"""Test namespace prefix management and expansion"""
|
||||
|
|
@ -216,63 +223,74 @@ class TestTripleConstructionLogic:
|
|||
def test_triple_validation(self):
|
||||
"""Test triple validation rules"""
|
||||
# Arrange
|
||||
def get_term_value(term):
|
||||
"""Extract value from a Term"""
|
||||
if term.type == IRI:
|
||||
return term.iri
|
||||
else:
|
||||
return term.value
|
||||
|
||||
def validate_triple(triple):
|
||||
errors = []
|
||||
|
||||
|
||||
# Check required components
|
||||
if not triple.s or not triple.s.value:
|
||||
s_val = get_term_value(triple.s) if triple.s else None
|
||||
p_val = get_term_value(triple.p) if triple.p else None
|
||||
o_val = get_term_value(triple.o) if triple.o else None
|
||||
|
||||
if not triple.s or not s_val:
|
||||
errors.append("Missing or empty subject")
|
||||
|
||||
if not triple.p or not triple.p.value:
|
||||
|
||||
if not triple.p or not p_val:
|
||||
errors.append("Missing or empty predicate")
|
||||
|
||||
if not triple.o or not triple.o.value:
|
||||
|
||||
if not triple.o or not o_val:
|
||||
errors.append("Missing or empty object")
|
||||
|
||||
|
||||
# Check URI validity for URI values
|
||||
uri_pattern = r'^https?://[^\s/$.?#].[^\s]*$'
|
||||
|
||||
if triple.s.is_uri and not re.match(uri_pattern, triple.s.value):
|
||||
|
||||
if triple.s.type == IRI and not re.match(uri_pattern, triple.s.iri or ""):
|
||||
errors.append("Invalid subject URI format")
|
||||
|
||||
if triple.p.is_uri and not re.match(uri_pattern, triple.p.value):
|
||||
|
||||
if triple.p.type == IRI and not re.match(uri_pattern, triple.p.iri or ""):
|
||||
errors.append("Invalid predicate URI format")
|
||||
|
||||
if triple.o.is_uri and not re.match(uri_pattern, triple.o.value):
|
||||
|
||||
if triple.o.type == IRI and not re.match(uri_pattern, triple.o.iri or ""):
|
||||
errors.append("Invalid object URI format")
|
||||
|
||||
|
||||
# Predicates should typically be URIs
|
||||
if not triple.p.is_uri:
|
||||
if triple.p.type != IRI:
|
||||
errors.append("Predicate should be a URI")
|
||||
|
||||
|
||||
return len(errors) == 0, errors
|
||||
|
||||
|
||||
# Test valid triple
|
||||
valid_triple = Triple(
|
||||
s=Value(value="http://trustgraph.ai/kg/person/john", is_uri=True, type=""),
|
||||
p=Value(value="http://schema.org/name", is_uri=True, type=""),
|
||||
o=Value(value="John Smith", is_uri=False, type="string")
|
||||
s=Term(type=IRI, iri="http://trustgraph.ai/kg/person/john"),
|
||||
p=Term(type=IRI, iri="http://schema.org/name"),
|
||||
o=Term(type=LITERAL, value="John Smith", datatype="string")
|
||||
)
|
||||
|
||||
|
||||
# Test invalid triples
|
||||
invalid_triples = [
|
||||
Triple(s=Value(value="", is_uri=True, type=""),
|
||||
p=Value(value="http://schema.org/name", is_uri=True, type=""),
|
||||
o=Value(value="John", is_uri=False, type="")), # Empty subject
|
||||
|
||||
Triple(s=Value(value="http://trustgraph.ai/kg/person/john", is_uri=True, type=""),
|
||||
p=Value(value="name", is_uri=False, type=""), # Non-URI predicate
|
||||
o=Value(value="John", is_uri=False, type="")),
|
||||
|
||||
Triple(s=Value(value="invalid-uri", is_uri=True, type=""),
|
||||
p=Value(value="http://schema.org/name", is_uri=True, type=""),
|
||||
o=Value(value="John", is_uri=False, type="")) # Invalid URI format
|
||||
Triple(s=Term(type=IRI, iri=""),
|
||||
p=Term(type=IRI, iri="http://schema.org/name"),
|
||||
o=Term(type=LITERAL, value="John")), # Empty subject
|
||||
|
||||
Triple(s=Term(type=IRI, iri="http://trustgraph.ai/kg/person/john"),
|
||||
p=Term(type=LITERAL, value="name"), # Non-URI predicate
|
||||
o=Term(type=LITERAL, value="John")),
|
||||
|
||||
Triple(s=Term(type=IRI, iri="invalid-uri"),
|
||||
p=Term(type=IRI, iri="http://schema.org/name"),
|
||||
o=Term(type=LITERAL, value="John")) # Invalid URI format
|
||||
]
|
||||
|
||||
|
||||
# Act & Assert
|
||||
is_valid, errors = validate_triple(valid_triple)
|
||||
assert is_valid, f"Valid triple failed validation: {errors}"
|
||||
|
||||
|
||||
for invalid_triple in invalid_triples:
|
||||
is_valid, errors = validate_triple(invalid_triple)
|
||||
assert not is_valid, f"Invalid triple passed validation: {invalid_triple}"
|
||||
|
|
@ -286,97 +304,97 @@ class TestTripleConstructionLogic:
|
|||
{"text": "OpenAI", "type": "ORG"},
|
||||
{"text": "San Francisco", "type": "PLACE"}
|
||||
]
|
||||
|
||||
|
||||
relationships = [
|
||||
{"subject": "John Smith", "predicate": "works_for", "object": "OpenAI"},
|
||||
{"subject": "OpenAI", "predicate": "located_in", "object": "San Francisco"}
|
||||
]
|
||||
|
||||
|
||||
def construct_triple_batch(entities, relationships, document_id="doc-1"):
|
||||
triples = []
|
||||
|
||||
|
||||
# Create type triples for entities
|
||||
for entity in entities:
|
||||
entity_uri = f"http://trustgraph.ai/kg/{entity['type'].lower()}/{entity['text'].lower().replace(' ', '-')}"
|
||||
type_uri = f"http://trustgraph.ai/kg/type/{entity['type']}"
|
||||
|
||||
|
||||
type_triple = Triple(
|
||||
s=Value(value=entity_uri, is_uri=True, type=""),
|
||||
p=Value(value="http://www.w3.org/1999/02/22-rdf-syntax-ns#type", is_uri=True, type=""),
|
||||
o=Value(value=type_uri, is_uri=True, type="")
|
||||
s=Term(type=IRI, iri=entity_uri),
|
||||
p=Term(type=IRI, iri="http://www.w3.org/1999/02/22-rdf-syntax-ns#type"),
|
||||
o=Term(type=IRI, iri=type_uri)
|
||||
)
|
||||
triples.append(type_triple)
|
||||
|
||||
|
||||
# Create relationship triples
|
||||
for rel in relationships:
|
||||
subject_uri = f"http://trustgraph.ai/kg/entity/{rel['subject'].lower().replace(' ', '-')}"
|
||||
object_uri = f"http://trustgraph.ai/kg/entity/{rel['object'].lower().replace(' ', '-')}"
|
||||
predicate_uri = f"http://schema.org/{rel['predicate'].replace('_', '')}"
|
||||
|
||||
|
||||
rel_triple = Triple(
|
||||
s=Value(value=subject_uri, is_uri=True, type=""),
|
||||
p=Value(value=predicate_uri, is_uri=True, type=""),
|
||||
o=Value(value=object_uri, is_uri=True, type="")
|
||||
s=Term(type=IRI, iri=subject_uri),
|
||||
p=Term(type=IRI, iri=predicate_uri),
|
||||
o=Term(type=IRI, iri=object_uri)
|
||||
)
|
||||
triples.append(rel_triple)
|
||||
|
||||
|
||||
return triples
|
||||
|
||||
|
||||
# Act
|
||||
triples = construct_triple_batch(entities, relationships)
|
||||
|
||||
|
||||
# Assert
|
||||
assert len(triples) == len(entities) + len(relationships) # Type triples + relationship triples
|
||||
|
||||
|
||||
# Check that all triples are valid Triple objects
|
||||
for triple in triples:
|
||||
assert isinstance(triple, Triple)
|
||||
assert triple.s.value != ""
|
||||
assert triple.p.value != ""
|
||||
assert triple.o.value != ""
|
||||
assert triple.s.iri != ""
|
||||
assert triple.p.iri != ""
|
||||
assert triple.o.iri != ""
|
||||
|
||||
def test_triples_batch_object_creation(self):
|
||||
"""Test creating Triples batch objects with metadata"""
|
||||
# Arrange
|
||||
sample_triples = [
|
||||
Triple(
|
||||
s=Value(value="http://trustgraph.ai/kg/person/john", is_uri=True, type=""),
|
||||
p=Value(value="http://schema.org/name", is_uri=True, type=""),
|
||||
o=Value(value="John Smith", is_uri=False, type="string")
|
||||
s=Term(type=IRI, iri="http://trustgraph.ai/kg/person/john"),
|
||||
p=Term(type=IRI, iri="http://schema.org/name"),
|
||||
o=Term(type=LITERAL, value="John Smith", datatype="string")
|
||||
),
|
||||
Triple(
|
||||
s=Value(value="http://trustgraph.ai/kg/person/john", is_uri=True, type=""),
|
||||
p=Value(value="http://schema.org/worksFor", is_uri=True, type=""),
|
||||
o=Value(value="http://trustgraph.ai/kg/org/openai", is_uri=True, type="")
|
||||
s=Term(type=IRI, iri="http://trustgraph.ai/kg/person/john"),
|
||||
p=Term(type=IRI, iri="http://schema.org/worksFor"),
|
||||
o=Term(type=IRI, iri="http://trustgraph.ai/kg/org/openai")
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
metadata = Metadata(
|
||||
id="test-doc-123",
|
||||
user="test_user",
|
||||
user="test_user",
|
||||
collection="test_collection",
|
||||
metadata=[]
|
||||
)
|
||||
|
||||
|
||||
# Act
|
||||
triples_batch = Triples(
|
||||
metadata=metadata,
|
||||
triples=sample_triples
|
||||
)
|
||||
|
||||
|
||||
# Assert
|
||||
assert isinstance(triples_batch, Triples)
|
||||
assert triples_batch.metadata.id == "test-doc-123"
|
||||
assert triples_batch.metadata.user == "test_user"
|
||||
assert triples_batch.metadata.collection == "test_collection"
|
||||
assert len(triples_batch.triples) == 2
|
||||
|
||||
|
||||
# Check that triples are properly embedded
|
||||
for triple in triples_batch.triples:
|
||||
assert isinstance(triple, Triple)
|
||||
assert isinstance(triple.s, Value)
|
||||
assert isinstance(triple.p, Value)
|
||||
assert isinstance(triple.o, Value)
|
||||
assert isinstance(triple.s, Term)
|
||||
assert isinstance(triple.p, Term)
|
||||
assert isinstance(triple.o, Term)
|
||||
|
||||
def test_uri_collision_handling(self):
|
||||
"""Test handling of URI collisions and duplicate detection"""
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import pytest
|
|||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from trustgraph.query.graph_embeddings.milvus.service import Processor
|
||||
from trustgraph.schema import Value, GraphEmbeddingsRequest
|
||||
from trustgraph.schema import Term, GraphEmbeddingsRequest, IRI, LITERAL
|
||||
|
||||
|
||||
class TestMilvusGraphEmbeddingsQueryProcessor:
|
||||
|
|
@ -68,50 +68,50 @@ class TestMilvusGraphEmbeddingsQueryProcessor:
|
|||
def test_create_value_with_http_uri(self, processor):
|
||||
"""Test create_value with HTTP URI"""
|
||||
result = processor.create_value("http://example.com/resource")
|
||||
|
||||
assert isinstance(result, Value)
|
||||
assert result.value == "http://example.com/resource"
|
||||
assert result.is_uri is True
|
||||
|
||||
assert isinstance(result, Term)
|
||||
assert result.iri == "http://example.com/resource"
|
||||
assert result.type == IRI
|
||||
|
||||
def test_create_value_with_https_uri(self, processor):
|
||||
"""Test create_value with HTTPS URI"""
|
||||
result = processor.create_value("https://example.com/resource")
|
||||
|
||||
assert isinstance(result, Value)
|
||||
assert result.value == "https://example.com/resource"
|
||||
assert result.is_uri is True
|
||||
|
||||
assert isinstance(result, Term)
|
||||
assert result.iri == "https://example.com/resource"
|
||||
assert result.type == IRI
|
||||
|
||||
def test_create_value_with_literal(self, processor):
|
||||
"""Test create_value with literal value"""
|
||||
result = processor.create_value("just a literal string")
|
||||
|
||||
assert isinstance(result, Value)
|
||||
assert isinstance(result, Term)
|
||||
assert result.value == "just a literal string"
|
||||
assert result.is_uri is False
|
||||
assert result.type == LITERAL
|
||||
|
||||
def test_create_value_with_empty_string(self, processor):
|
||||
"""Test create_value with empty string"""
|
||||
result = processor.create_value("")
|
||||
|
||||
assert isinstance(result, Value)
|
||||
assert isinstance(result, Term)
|
||||
assert result.value == ""
|
||||
assert result.is_uri is False
|
||||
assert result.type == LITERAL
|
||||
|
||||
def test_create_value_with_partial_uri(self, processor):
|
||||
"""Test create_value with string that looks like URI but isn't complete"""
|
||||
result = processor.create_value("http")
|
||||
|
||||
assert isinstance(result, Value)
|
||||
assert isinstance(result, Term)
|
||||
assert result.value == "http"
|
||||
assert result.is_uri is False
|
||||
assert result.type == LITERAL
|
||||
|
||||
def test_create_value_with_ftp_uri(self, processor):
|
||||
"""Test create_value with FTP URI (should not be detected as URI)"""
|
||||
result = processor.create_value("ftp://example.com/file")
|
||||
|
||||
assert isinstance(result, Value)
|
||||
assert isinstance(result, Term)
|
||||
assert result.value == "ftp://example.com/file"
|
||||
assert result.is_uri is False
|
||||
assert result.type == LITERAL
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_query_graph_embeddings_single_vector(self, processor):
|
||||
|
|
@ -138,17 +138,17 @@ class TestMilvusGraphEmbeddingsQueryProcessor:
|
|||
[0.1, 0.2, 0.3], 'test_user', 'test_collection', limit=10
|
||||
)
|
||||
|
||||
# Verify results are converted to Value objects
|
||||
# Verify results are converted to Term objects
|
||||
assert len(result) == 3
|
||||
assert isinstance(result[0], Value)
|
||||
assert result[0].value == "http://example.com/entity1"
|
||||
assert result[0].is_uri is True
|
||||
assert isinstance(result[1], Value)
|
||||
assert result[1].value == "http://example.com/entity2"
|
||||
assert result[1].is_uri is True
|
||||
assert isinstance(result[2], Value)
|
||||
assert isinstance(result[0], Term)
|
||||
assert result[0].iri == "http://example.com/entity1"
|
||||
assert result[0].type == IRI
|
||||
assert isinstance(result[1], Term)
|
||||
assert result[1].iri == "http://example.com/entity2"
|
||||
assert result[1].type == IRI
|
||||
assert isinstance(result[2], Term)
|
||||
assert result[2].value == "literal entity"
|
||||
assert result[2].is_uri is False
|
||||
assert result[2].type == LITERAL
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_query_graph_embeddings_multiple_vectors(self, processor):
|
||||
|
|
@ -186,7 +186,7 @@ class TestMilvusGraphEmbeddingsQueryProcessor:
|
|||
|
||||
# Verify results are deduplicated and limited
|
||||
assert len(result) == 3
|
||||
entity_values = [r.value for r in result]
|
||||
entity_values = [r.iri if r.type == IRI else r.value for r in result]
|
||||
assert "http://example.com/entity1" in entity_values
|
||||
assert "http://example.com/entity2" in entity_values
|
||||
assert "http://example.com/entity3" in entity_values
|
||||
|
|
@ -246,7 +246,7 @@ class TestMilvusGraphEmbeddingsQueryProcessor:
|
|||
|
||||
# Verify duplicates are removed
|
||||
assert len(result) == 3
|
||||
entity_values = [r.value for r in result]
|
||||
entity_values = [r.iri if r.type == IRI else r.value for r in result]
|
||||
assert len(set(entity_values)) == 3 # All unique
|
||||
assert "http://example.com/entity1" in entity_values
|
||||
assert "http://example.com/entity2" in entity_values
|
||||
|
|
@ -346,14 +346,14 @@ class TestMilvusGraphEmbeddingsQueryProcessor:
|
|||
assert len(result) == 4
|
||||
|
||||
# Check URI entities
|
||||
uri_results = [r for r in result if r.is_uri]
|
||||
uri_results = [r for r in result if r.type == IRI]
|
||||
assert len(uri_results) == 2
|
||||
uri_values = [r.value for r in uri_results]
|
||||
uri_values = [r.iri for r in uri_results]
|
||||
assert "http://example.com/uri_entity" in uri_values
|
||||
assert "https://example.com/another_uri" in uri_values
|
||||
|
||||
# Check literal entities
|
||||
literal_results = [r for r in result if not r.is_uri]
|
||||
literal_results = [r for r in result if not r.type == IRI]
|
||||
assert len(literal_results) == 2
|
||||
literal_values = [r.value for r in literal_results]
|
||||
assert "literal entity text" in literal_values
|
||||
|
|
@ -486,7 +486,7 @@ class TestMilvusGraphEmbeddingsQueryProcessor:
|
|||
|
||||
# Verify results from all dimensions
|
||||
assert len(result) == 3
|
||||
entity_values = [r.value for r in result]
|
||||
entity_values = [r.iri if r.type == IRI else r.value for r in result]
|
||||
assert "entity_2d" in entity_values
|
||||
assert "entity_4d" in entity_values
|
||||
assert "entity_3d" in entity_values
|
||||
|
|
@ -9,7 +9,7 @@ from unittest.mock import MagicMock, patch
|
|||
pytest.skip("Pinecone library missing protoc_gen_openapiv2 dependency", allow_module_level=True)
|
||||
|
||||
from trustgraph.query.graph_embeddings.pinecone.service import Processor
|
||||
from trustgraph.schema import Value
|
||||
from trustgraph.schema import Term, IRI, LITERAL
|
||||
|
||||
|
||||
class TestPineconeGraphEmbeddingsQueryProcessor:
|
||||
|
|
@ -105,27 +105,27 @@ class TestPineconeGraphEmbeddingsQueryProcessor:
|
|||
uri_entity = "http://example.org/entity"
|
||||
value = processor.create_value(uri_entity)
|
||||
|
||||
assert isinstance(value, Value)
|
||||
assert isinstance(value, Term)
|
||||
assert value.value == uri_entity
|
||||
assert value.is_uri == True
|
||||
assert value.type == IRI
|
||||
|
||||
def test_create_value_https_uri(self, processor):
|
||||
"""Test create_value method for HTTPS URI entities"""
|
||||
uri_entity = "https://example.org/entity"
|
||||
value = processor.create_value(uri_entity)
|
||||
|
||||
assert isinstance(value, Value)
|
||||
assert isinstance(value, Term)
|
||||
assert value.value == uri_entity
|
||||
assert value.is_uri == True
|
||||
assert value.type == IRI
|
||||
|
||||
def test_create_value_literal(self, processor):
|
||||
"""Test create_value method for literal entities"""
|
||||
literal_entity = "literal_entity"
|
||||
value = processor.create_value(literal_entity)
|
||||
|
||||
assert isinstance(value, Value)
|
||||
assert isinstance(value, Term)
|
||||
assert value.value == literal_entity
|
||||
assert value.is_uri == False
|
||||
assert value.type == LITERAL
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_query_graph_embeddings_single_vector(self, processor):
|
||||
|
|
@ -165,11 +165,11 @@ class TestPineconeGraphEmbeddingsQueryProcessor:
|
|||
# Verify results
|
||||
assert len(entities) == 3
|
||||
assert entities[0].value == 'http://example.org/entity1'
|
||||
assert entities[0].is_uri == True
|
||||
assert entities[0].type == IRI
|
||||
assert entities[1].value == 'entity2'
|
||||
assert entities[1].is_uri == False
|
||||
assert entities[1].type == LITERAL
|
||||
assert entities[2].value == 'http://example.org/entity3'
|
||||
assert entities[2].is_uri == True
|
||||
assert entities[2].type == IRI
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_query_graph_embeddings_multiple_vectors(self, processor, mock_query_message):
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ from unittest import IsolatedAsyncioTestCase
|
|||
|
||||
# Import the service under test
|
||||
from trustgraph.query.graph_embeddings.qdrant.service import Processor
|
||||
from trustgraph.schema import IRI, LITERAL
|
||||
|
||||
|
||||
class TestQdrantGraphEmbeddingsQuery(IsolatedAsyncioTestCase):
|
||||
|
|
@ -85,10 +86,10 @@ class TestQdrantGraphEmbeddingsQuery(IsolatedAsyncioTestCase):
|
|||
value = processor.create_value('http://example.com/entity')
|
||||
|
||||
# Assert
|
||||
assert hasattr(value, 'value')
|
||||
assert value.value == 'http://example.com/entity'
|
||||
assert hasattr(value, 'is_uri')
|
||||
assert value.is_uri == True
|
||||
assert hasattr(value, 'iri')
|
||||
assert value.iri == 'http://example.com/entity'
|
||||
assert hasattr(value, 'type')
|
||||
assert value.type == IRI
|
||||
|
||||
@patch('trustgraph.query.graph_embeddings.qdrant.service.QdrantClient')
|
||||
@patch('trustgraph.base.GraphEmbeddingsQueryService.__init__')
|
||||
|
|
@ -109,10 +110,10 @@ class TestQdrantGraphEmbeddingsQuery(IsolatedAsyncioTestCase):
|
|||
value = processor.create_value('https://secure.example.com/entity')
|
||||
|
||||
# Assert
|
||||
assert hasattr(value, 'value')
|
||||
assert value.value == 'https://secure.example.com/entity'
|
||||
assert hasattr(value, 'is_uri')
|
||||
assert value.is_uri == True
|
||||
assert hasattr(value, 'iri')
|
||||
assert value.iri == 'https://secure.example.com/entity'
|
||||
assert hasattr(value, 'type')
|
||||
assert value.type == IRI
|
||||
|
||||
@patch('trustgraph.query.graph_embeddings.qdrant.service.QdrantClient')
|
||||
@patch('trustgraph.base.GraphEmbeddingsQueryService.__init__')
|
||||
|
|
@ -135,8 +136,8 @@ class TestQdrantGraphEmbeddingsQuery(IsolatedAsyncioTestCase):
|
|||
# Assert
|
||||
assert hasattr(value, 'value')
|
||||
assert value.value == 'regular entity name'
|
||||
assert hasattr(value, 'is_uri')
|
||||
assert value.is_uri == False
|
||||
assert hasattr(value, 'type')
|
||||
assert value.type == LITERAL
|
||||
|
||||
@patch('trustgraph.query.graph_embeddings.qdrant.service.QdrantClient')
|
||||
@patch('trustgraph.base.GraphEmbeddingsQueryService.__init__')
|
||||
|
|
@ -428,14 +429,14 @@ class TestQdrantGraphEmbeddingsQuery(IsolatedAsyncioTestCase):
|
|||
assert len(result) == 3
|
||||
|
||||
# Check URI entities
|
||||
uri_entities = [entity for entity in result if hasattr(entity, 'is_uri') and entity.is_uri]
|
||||
uri_entities = [entity for entity in result if entity.type == IRI]
|
||||
assert len(uri_entities) == 2
|
||||
uri_values = [entity.value for entity in uri_entities]
|
||||
uri_values = [entity.iri for entity in uri_entities]
|
||||
assert 'http://example.com/entity1' in uri_values
|
||||
assert 'https://secure.example.com/entity2' in uri_values
|
||||
|
||||
|
||||
# Check regular entities
|
||||
regular_entities = [entity for entity in result if hasattr(entity, 'is_uri') and not entity.is_uri]
|
||||
regular_entities = [entity for entity in result if entity.type == LITERAL]
|
||||
assert len(regular_entities) == 1
|
||||
assert regular_entities[0].value == 'regular entity'
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import pytest
|
|||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from trustgraph.query.triples.memgraph.service import Processor
|
||||
from trustgraph.schema import TriplesQueryRequest, Value
|
||||
from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL
|
||||
|
||||
|
||||
class TestMemgraphQueryUserCollectionIsolation:
|
||||
|
|
@ -24,9 +24,9 @@ class TestMemgraphQueryUserCollectionIsolation:
|
|||
query = TriplesQueryRequest(
|
||||
user="test_user",
|
||||
collection="test_collection",
|
||||
s=Value(value="http://example.com/s", is_uri=True),
|
||||
p=Value(value="http://example.com/p", is_uri=True),
|
||||
o=Value(value="test_object", is_uri=False),
|
||||
s=Term(type=IRI, iri="http://example.com/s"),
|
||||
p=Term(type=IRI, iri="http://example.com/p"),
|
||||
o=Term(type=LITERAL, value="test_object"),
|
||||
limit=1000
|
||||
)
|
||||
|
||||
|
|
@ -65,8 +65,8 @@ class TestMemgraphQueryUserCollectionIsolation:
|
|||
query = TriplesQueryRequest(
|
||||
user="test_user",
|
||||
collection="test_collection",
|
||||
s=Value(value="http://example.com/s", is_uri=True),
|
||||
p=Value(value="http://example.com/p", is_uri=True),
|
||||
s=Term(type=IRI, iri="http://example.com/s"),
|
||||
p=Term(type=IRI, iri="http://example.com/p"),
|
||||
o=None,
|
||||
limit=1000
|
||||
)
|
||||
|
|
@ -105,9 +105,9 @@ class TestMemgraphQueryUserCollectionIsolation:
|
|||
query = TriplesQueryRequest(
|
||||
user="test_user",
|
||||
collection="test_collection",
|
||||
s=Value(value="http://example.com/s", is_uri=True),
|
||||
s=Term(type=IRI, iri="http://example.com/s"),
|
||||
p=None,
|
||||
o=Value(value="http://example.com/o", is_uri=True),
|
||||
o=Term(type=IRI, iri="http://example.com/o"),
|
||||
limit=1000
|
||||
)
|
||||
|
||||
|
|
@ -145,7 +145,7 @@ class TestMemgraphQueryUserCollectionIsolation:
|
|||
query = TriplesQueryRequest(
|
||||
user="test_user",
|
||||
collection="test_collection",
|
||||
s=Value(value="http://example.com/s", is_uri=True),
|
||||
s=Term(type=IRI, iri="http://example.com/s"),
|
||||
p=None,
|
||||
o=None,
|
||||
limit=1000
|
||||
|
|
@ -185,8 +185,8 @@ class TestMemgraphQueryUserCollectionIsolation:
|
|||
user="test_user",
|
||||
collection="test_collection",
|
||||
s=None,
|
||||
p=Value(value="http://example.com/p", is_uri=True),
|
||||
o=Value(value="literal", is_uri=False),
|
||||
p=Term(type=IRI, iri="http://example.com/p"),
|
||||
o=Term(type=LITERAL, value="literal"),
|
||||
limit=1000
|
||||
)
|
||||
|
||||
|
|
@ -225,7 +225,7 @@ class TestMemgraphQueryUserCollectionIsolation:
|
|||
user="test_user",
|
||||
collection="test_collection",
|
||||
s=None,
|
||||
p=Value(value="http://example.com/p", is_uri=True),
|
||||
p=Term(type=IRI, iri="http://example.com/p"),
|
||||
o=None,
|
||||
limit=1000
|
||||
)
|
||||
|
|
@ -265,7 +265,7 @@ class TestMemgraphQueryUserCollectionIsolation:
|
|||
collection="test_collection",
|
||||
s=None,
|
||||
p=None,
|
||||
o=Value(value="test_value", is_uri=False),
|
||||
o=Term(type=LITERAL, value="test_value"),
|
||||
limit=1000
|
||||
)
|
||||
|
||||
|
|
@ -355,7 +355,7 @@ class TestMemgraphQueryUserCollectionIsolation:
|
|||
|
||||
# Query without user/collection fields
|
||||
query = TriplesQueryRequest(
|
||||
s=Value(value="http://example.com/s", is_uri=True),
|
||||
s=Term(type=IRI, iri="http://example.com/s"),
|
||||
p=None,
|
||||
o=None,
|
||||
limit=1000
|
||||
|
|
@ -385,7 +385,7 @@ class TestMemgraphQueryUserCollectionIsolation:
|
|||
query = TriplesQueryRequest(
|
||||
user="test_user",
|
||||
collection="test_collection",
|
||||
s=Value(value="http://example.com/s", is_uri=True),
|
||||
s=Term(type=IRI, iri="http://example.com/s"),
|
||||
p=None,
|
||||
o=None,
|
||||
limit=1000
|
||||
|
|
@ -416,17 +416,17 @@ class TestMemgraphQueryUserCollectionIsolation:
|
|||
assert len(result) == 2
|
||||
|
||||
# First triple (literal object)
|
||||
assert result[0].s.value == "http://example.com/s"
|
||||
assert result[0].s.is_uri == True
|
||||
assert result[0].p.value == "http://example.com/p1"
|
||||
assert result[0].p.is_uri == True
|
||||
assert result[0].s.iri == "http://example.com/s"
|
||||
assert result[0].s.type == IRI
|
||||
assert result[0].p.iri == "http://example.com/p1"
|
||||
assert result[0].p.type == IRI
|
||||
assert result[0].o.value == "literal_value"
|
||||
assert result[0].o.is_uri == False
|
||||
|
||||
assert result[0].o.type == LITERAL
|
||||
|
||||
# Second triple (URI object)
|
||||
assert result[1].s.value == "http://example.com/s"
|
||||
assert result[1].s.is_uri == True
|
||||
assert result[1].p.value == "http://example.com/p2"
|
||||
assert result[1].p.is_uri == True
|
||||
assert result[1].o.value == "http://example.com/o"
|
||||
assert result[1].o.is_uri == True
|
||||
assert result[1].s.iri == "http://example.com/s"
|
||||
assert result[1].s.type == IRI
|
||||
assert result[1].p.iri == "http://example.com/p2"
|
||||
assert result[1].p.type == IRI
|
||||
assert result[1].o.iri == "http://example.com/o"
|
||||
assert result[1].o.type == IRI
|
||||
|
|
@ -6,7 +6,7 @@ import pytest
|
|||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from trustgraph.query.triples.neo4j.service import Processor
|
||||
from trustgraph.schema import TriplesQueryRequest, Value
|
||||
from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL
|
||||
|
||||
|
||||
class TestNeo4jQueryUserCollectionIsolation:
|
||||
|
|
@ -24,9 +24,9 @@ class TestNeo4jQueryUserCollectionIsolation:
|
|||
query = TriplesQueryRequest(
|
||||
user="test_user",
|
||||
collection="test_collection",
|
||||
s=Value(value="http://example.com/s", is_uri=True),
|
||||
p=Value(value="http://example.com/p", is_uri=True),
|
||||
o=Value(value="test_object", is_uri=False),
|
||||
s=Term(type=IRI, iri="http://example.com/s"),
|
||||
p=Term(type=IRI, iri="http://example.com/p"),
|
||||
o=Term(type=LITERAL, value="test_object"),
|
||||
limit=10
|
||||
)
|
||||
|
||||
|
|
@ -65,8 +65,8 @@ class TestNeo4jQueryUserCollectionIsolation:
|
|||
query = TriplesQueryRequest(
|
||||
user="test_user",
|
||||
collection="test_collection",
|
||||
s=Value(value="http://example.com/s", is_uri=True),
|
||||
p=Value(value="http://example.com/p", is_uri=True),
|
||||
s=Term(type=IRI, iri="http://example.com/s"),
|
||||
p=Term(type=IRI, iri="http://example.com/p"),
|
||||
o=None,
|
||||
limit=10
|
||||
)
|
||||
|
|
@ -123,9 +123,9 @@ class TestNeo4jQueryUserCollectionIsolation:
|
|||
query = TriplesQueryRequest(
|
||||
user="test_user",
|
||||
collection="test_collection",
|
||||
s=Value(value="http://example.com/s", is_uri=True),
|
||||
s=Term(type=IRI, iri="http://example.com/s"),
|
||||
p=None,
|
||||
o=Value(value="http://example.com/o", is_uri=True),
|
||||
o=Term(type=IRI, iri="http://example.com/o"),
|
||||
limit=10
|
||||
)
|
||||
|
||||
|
|
@ -163,7 +163,7 @@ class TestNeo4jQueryUserCollectionIsolation:
|
|||
query = TriplesQueryRequest(
|
||||
user="test_user",
|
||||
collection="test_collection",
|
||||
s=Value(value="http://example.com/s", is_uri=True),
|
||||
s=Term(type=IRI, iri="http://example.com/s"),
|
||||
p=None,
|
||||
o=None,
|
||||
limit=10
|
||||
|
|
@ -203,8 +203,8 @@ class TestNeo4jQueryUserCollectionIsolation:
|
|||
user="test_user",
|
||||
collection="test_collection",
|
||||
s=None,
|
||||
p=Value(value="http://example.com/p", is_uri=True),
|
||||
o=Value(value="literal", is_uri=False),
|
||||
p=Term(type=IRI, iri="http://example.com/p"),
|
||||
o=Term(type=LITERAL, value="literal"),
|
||||
limit=10
|
||||
)
|
||||
|
||||
|
|
@ -243,7 +243,7 @@ class TestNeo4jQueryUserCollectionIsolation:
|
|||
user="test_user",
|
||||
collection="test_collection",
|
||||
s=None,
|
||||
p=Value(value="http://example.com/p", is_uri=True),
|
||||
p=Term(type=IRI, iri="http://example.com/p"),
|
||||
o=None,
|
||||
limit=10
|
||||
)
|
||||
|
|
@ -283,7 +283,7 @@ class TestNeo4jQueryUserCollectionIsolation:
|
|||
collection="test_collection",
|
||||
s=None,
|
||||
p=None,
|
||||
o=Value(value="test_value", is_uri=False),
|
||||
o=Term(type=LITERAL, value="test_value"),
|
||||
limit=10
|
||||
)
|
||||
|
||||
|
|
@ -373,7 +373,7 @@ class TestNeo4jQueryUserCollectionIsolation:
|
|||
|
||||
# Query without user/collection fields
|
||||
query = TriplesQueryRequest(
|
||||
s=Value(value="http://example.com/s", is_uri=True),
|
||||
s=Term(type=IRI, iri="http://example.com/s"),
|
||||
p=None,
|
||||
o=None,
|
||||
limit=10
|
||||
|
|
@ -403,7 +403,7 @@ class TestNeo4jQueryUserCollectionIsolation:
|
|||
query = TriplesQueryRequest(
|
||||
user="test_user",
|
||||
collection="test_collection",
|
||||
s=Value(value="http://example.com/s", is_uri=True),
|
||||
s=Term(type=IRI, iri="http://example.com/s"),
|
||||
p=None,
|
||||
o=None,
|
||||
limit=10
|
||||
|
|
@ -434,17 +434,17 @@ class TestNeo4jQueryUserCollectionIsolation:
|
|||
assert len(result) == 2
|
||||
|
||||
# First triple (literal object)
|
||||
assert result[0].s.value == "http://example.com/s"
|
||||
assert result[0].s.is_uri == True
|
||||
assert result[0].p.value == "http://example.com/p1"
|
||||
assert result[0].p.is_uri == True
|
||||
assert result[0].s.iri == "http://example.com/s"
|
||||
assert result[0].s.type == IRI
|
||||
assert result[0].p.iri == "http://example.com/p1"
|
||||
assert result[0].p.type == IRI
|
||||
assert result[0].o.value == "literal_value"
|
||||
assert result[0].o.is_uri == False
|
||||
|
||||
assert result[0].o.type == LITERAL
|
||||
|
||||
# Second triple (URI object)
|
||||
assert result[1].s.value == "http://example.com/s"
|
||||
assert result[1].s.is_uri == True
|
||||
assert result[1].p.value == "http://example.com/p2"
|
||||
assert result[1].p.is_uri == True
|
||||
assert result[1].o.value == "http://example.com/o"
|
||||
assert result[1].o.is_uri == True
|
||||
assert result[1].s.iri == "http://example.com/s"
|
||||
assert result[1].s.type == IRI
|
||||
assert result[1].p.iri == "http://example.com/p2"
|
||||
assert result[1].p.type == IRI
|
||||
assert result[1].o.iri == "http://example.com/o"
|
||||
assert result[1].o.type == IRI
|
||||
|
|
@ -5,8 +5,8 @@ Tests for Cassandra triples query service
|
|||
import pytest
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from trustgraph.query.triples.cassandra.service import Processor
|
||||
from trustgraph.schema import Value
|
||||
from trustgraph.query.triples.cassandra.service import Processor, create_term
|
||||
from trustgraph.schema import Term, IRI, LITERAL
|
||||
|
||||
|
||||
class TestCassandraQueryProcessor:
|
||||
|
|
@ -21,64 +21,67 @@ class TestCassandraQueryProcessor:
|
|||
graph_host='localhost'
|
||||
)
|
||||
|
||||
def test_create_value_with_http_uri(self, processor):
|
||||
"""Test create_value with HTTP URI"""
|
||||
result = processor.create_value("http://example.com/resource")
|
||||
|
||||
assert isinstance(result, Value)
|
||||
assert result.value == "http://example.com/resource"
|
||||
assert result.is_uri is True
|
||||
def test_create_term_with_http_uri(self, processor):
|
||||
"""Test create_term with HTTP URI"""
|
||||
result = create_term("http://example.com/resource")
|
||||
|
||||
def test_create_value_with_https_uri(self, processor):
|
||||
"""Test create_value with HTTPS URI"""
|
||||
result = processor.create_value("https://example.com/resource")
|
||||
|
||||
assert isinstance(result, Value)
|
||||
assert result.value == "https://example.com/resource"
|
||||
assert result.is_uri is True
|
||||
assert isinstance(result, Term)
|
||||
assert result.iri == "http://example.com/resource"
|
||||
assert result.type == IRI
|
||||
|
||||
def test_create_value_with_literal(self, processor):
|
||||
"""Test create_value with literal value"""
|
||||
result = processor.create_value("just a literal string")
|
||||
|
||||
assert isinstance(result, Value)
|
||||
def test_create_term_with_https_uri(self, processor):
|
||||
"""Test create_term with HTTPS URI"""
|
||||
result = create_term("https://example.com/resource")
|
||||
|
||||
assert isinstance(result, Term)
|
||||
assert result.iri == "https://example.com/resource"
|
||||
assert result.type == IRI
|
||||
|
||||
def test_create_term_with_literal(self, processor):
|
||||
"""Test create_term with literal value"""
|
||||
result = create_term("just a literal string")
|
||||
|
||||
assert isinstance(result, Term)
|
||||
assert result.value == "just a literal string"
|
||||
assert result.is_uri is False
|
||||
assert result.type == LITERAL
|
||||
|
||||
def test_create_value_with_empty_string(self, processor):
|
||||
"""Test create_value with empty string"""
|
||||
result = processor.create_value("")
|
||||
|
||||
assert isinstance(result, Value)
|
||||
def test_create_term_with_empty_string(self, processor):
|
||||
"""Test create_term with empty string"""
|
||||
result = create_term("")
|
||||
|
||||
assert isinstance(result, Term)
|
||||
assert result.value == ""
|
||||
assert result.is_uri is False
|
||||
assert result.type == LITERAL
|
||||
|
||||
def test_create_value_with_partial_uri(self, processor):
|
||||
"""Test create_value with string that looks like URI but isn't complete"""
|
||||
result = processor.create_value("http")
|
||||
|
||||
assert isinstance(result, Value)
|
||||
def test_create_term_with_partial_uri(self, processor):
|
||||
"""Test create_term with string that looks like URI but isn't complete"""
|
||||
result = create_term("http")
|
||||
|
||||
assert isinstance(result, Term)
|
||||
assert result.value == "http"
|
||||
assert result.is_uri is False
|
||||
assert result.type == LITERAL
|
||||
|
||||
def test_create_value_with_ftp_uri(self, processor):
|
||||
"""Test create_value with FTP URI (should not be detected as URI)"""
|
||||
result = processor.create_value("ftp://example.com/file")
|
||||
|
||||
assert isinstance(result, Value)
|
||||
def test_create_term_with_ftp_uri(self, processor):
|
||||
"""Test create_term with FTP URI (should not be detected as URI)"""
|
||||
result = create_term("ftp://example.com/file")
|
||||
|
||||
assert isinstance(result, Term)
|
||||
assert result.value == "ftp://example.com/file"
|
||||
assert result.is_uri is False
|
||||
assert result.type == LITERAL
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch('trustgraph.query.triples.cassandra.service.KnowledgeGraph')
|
||||
async def test_query_triples_spo_query(self, mock_trustgraph):
|
||||
"""Test querying triples with subject, predicate, and object specified"""
|
||||
from trustgraph.schema import TriplesQueryRequest, Value
|
||||
from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL
|
||||
|
||||
# Setup mock TrustGraph
|
||||
mock_tg_instance = MagicMock()
|
||||
mock_trustgraph.return_value = mock_tg_instance
|
||||
mock_tg_instance.get_spo.return_value = None # SPO query returns None if found
|
||||
# SPO query returns a list of results (with mock graph attribute)
|
||||
mock_result = MagicMock()
|
||||
mock_result.g = None
|
||||
mock_tg_instance.get_spo.return_value = [mock_result]
|
||||
|
||||
processor = Processor(
|
||||
taskgroup=MagicMock(),
|
||||
|
|
@ -90,9 +93,9 @@ class TestCassandraQueryProcessor:
|
|||
query = TriplesQueryRequest(
|
||||
user='test_user',
|
||||
collection='test_collection',
|
||||
s=Value(value='test_subject', is_uri=False),
|
||||
p=Value(value='test_predicate', is_uri=False),
|
||||
o=Value(value='test_object', is_uri=False),
|
||||
s=Term(type=LITERAL, value='test_subject'),
|
||||
p=Term(type=LITERAL, value='test_predicate'),
|
||||
o=Term(type=LITERAL, value='test_object'),
|
||||
limit=100
|
||||
)
|
||||
|
||||
|
|
@ -106,7 +109,7 @@ class TestCassandraQueryProcessor:
|
|||
|
||||
# Verify get_spo was called with correct parameters
|
||||
mock_tg_instance.get_spo.assert_called_once_with(
|
||||
'test_collection', 'test_subject', 'test_predicate', 'test_object', limit=100
|
||||
'test_collection', 'test_subject', 'test_predicate', 'test_object', g=None, limit=100
|
||||
)
|
||||
|
||||
# Verify result contains the queried triple
|
||||
|
|
@ -146,7 +149,7 @@ class TestCassandraQueryProcessor:
|
|||
@patch('trustgraph.query.triples.cassandra.service.KnowledgeGraph')
|
||||
async def test_query_triples_sp_pattern(self, mock_trustgraph):
|
||||
"""Test SP query pattern (subject and predicate, no object)"""
|
||||
from trustgraph.schema import TriplesQueryRequest, Value
|
||||
from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL
|
||||
|
||||
# Setup mock TrustGraph and response
|
||||
mock_tg_instance = MagicMock()
|
||||
|
|
@ -161,15 +164,15 @@ class TestCassandraQueryProcessor:
|
|||
query = TriplesQueryRequest(
|
||||
user='test_user',
|
||||
collection='test_collection',
|
||||
s=Value(value='test_subject', is_uri=False),
|
||||
p=Value(value='test_predicate', is_uri=False),
|
||||
s=Term(type=LITERAL, value='test_subject'),
|
||||
p=Term(type=LITERAL, value='test_predicate'),
|
||||
o=None,
|
||||
limit=50
|
||||
)
|
||||
|
||||
result = await processor.query_triples(query)
|
||||
|
||||
mock_tg_instance.get_sp.assert_called_once_with('test_collection', 'test_subject', 'test_predicate', limit=50)
|
||||
mock_tg_instance.get_sp.assert_called_once_with('test_collection', 'test_subject', 'test_predicate', g=None, limit=50)
|
||||
assert len(result) == 1
|
||||
assert result[0].s.value == 'test_subject'
|
||||
assert result[0].p.value == 'test_predicate'
|
||||
|
|
@ -179,7 +182,7 @@ class TestCassandraQueryProcessor:
|
|||
@patch('trustgraph.query.triples.cassandra.service.KnowledgeGraph')
|
||||
async def test_query_triples_s_pattern(self, mock_trustgraph):
|
||||
"""Test S query pattern (subject only)"""
|
||||
from trustgraph.schema import TriplesQueryRequest, Value
|
||||
from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL
|
||||
|
||||
mock_tg_instance = MagicMock()
|
||||
mock_trustgraph.return_value = mock_tg_instance
|
||||
|
|
@ -194,7 +197,7 @@ class TestCassandraQueryProcessor:
|
|||
query = TriplesQueryRequest(
|
||||
user='test_user',
|
||||
collection='test_collection',
|
||||
s=Value(value='test_subject', is_uri=False),
|
||||
s=Term(type=LITERAL, value='test_subject'),
|
||||
p=None,
|
||||
o=None,
|
||||
limit=25
|
||||
|
|
@ -202,7 +205,7 @@ class TestCassandraQueryProcessor:
|
|||
|
||||
result = await processor.query_triples(query)
|
||||
|
||||
mock_tg_instance.get_s.assert_called_once_with('test_collection', 'test_subject', limit=25)
|
||||
mock_tg_instance.get_s.assert_called_once_with('test_collection', 'test_subject', g=None, limit=25)
|
||||
assert len(result) == 1
|
||||
assert result[0].s.value == 'test_subject'
|
||||
assert result[0].p.value == 'result_predicate'
|
||||
|
|
@ -212,7 +215,7 @@ class TestCassandraQueryProcessor:
|
|||
@patch('trustgraph.query.triples.cassandra.service.KnowledgeGraph')
|
||||
async def test_query_triples_p_pattern(self, mock_trustgraph):
|
||||
"""Test P query pattern (predicate only)"""
|
||||
from trustgraph.schema import TriplesQueryRequest, Value
|
||||
from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL
|
||||
|
||||
mock_tg_instance = MagicMock()
|
||||
mock_trustgraph.return_value = mock_tg_instance
|
||||
|
|
@ -228,14 +231,14 @@ class TestCassandraQueryProcessor:
|
|||
user='test_user',
|
||||
collection='test_collection',
|
||||
s=None,
|
||||
p=Value(value='test_predicate', is_uri=False),
|
||||
p=Term(type=LITERAL, value='test_predicate'),
|
||||
o=None,
|
||||
limit=10
|
||||
)
|
||||
|
||||
result = await processor.query_triples(query)
|
||||
|
||||
mock_tg_instance.get_p.assert_called_once_with('test_collection', 'test_predicate', limit=10)
|
||||
mock_tg_instance.get_p.assert_called_once_with('test_collection', 'test_predicate', g=None, limit=10)
|
||||
assert len(result) == 1
|
||||
assert result[0].s.value == 'result_subject'
|
||||
assert result[0].p.value == 'test_predicate'
|
||||
|
|
@ -245,7 +248,7 @@ class TestCassandraQueryProcessor:
|
|||
@patch('trustgraph.query.triples.cassandra.service.KnowledgeGraph')
|
||||
async def test_query_triples_o_pattern(self, mock_trustgraph):
|
||||
"""Test O query pattern (object only)"""
|
||||
from trustgraph.schema import TriplesQueryRequest, Value
|
||||
from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL
|
||||
|
||||
mock_tg_instance = MagicMock()
|
||||
mock_trustgraph.return_value = mock_tg_instance
|
||||
|
|
@ -262,13 +265,13 @@ class TestCassandraQueryProcessor:
|
|||
collection='test_collection',
|
||||
s=None,
|
||||
p=None,
|
||||
o=Value(value='test_object', is_uri=False),
|
||||
o=Term(type=LITERAL, value='test_object'),
|
||||
limit=75
|
||||
)
|
||||
|
||||
result = await processor.query_triples(query)
|
||||
|
||||
mock_tg_instance.get_o.assert_called_once_with('test_collection', 'test_object', limit=75)
|
||||
mock_tg_instance.get_o.assert_called_once_with('test_collection', 'test_object', g=None, limit=75)
|
||||
assert len(result) == 1
|
||||
assert result[0].s.value == 'result_subject'
|
||||
assert result[0].p.value == 'result_predicate'
|
||||
|
|
@ -372,18 +375,21 @@ class TestCassandraQueryProcessor:
|
|||
|
||||
run()
|
||||
|
||||
mock_launch.assert_called_once_with(default_ident, '\nTriples query service. Input is a (s, p, o) triple, some values may be\nnull. Output is a list of triples.\n')
|
||||
mock_launch.assert_called_once_with(default_ident, '\nTriples query service. Input is a (s, p, o, g) quad pattern, some values may be\nnull. Output is a list of quads.\n')
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch('trustgraph.query.triples.cassandra.service.KnowledgeGraph')
|
||||
async def test_query_triples_with_authentication(self, mock_trustgraph):
|
||||
"""Test querying with username and password authentication"""
|
||||
from trustgraph.schema import TriplesQueryRequest, Value
|
||||
from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL
|
||||
|
||||
mock_tg_instance = MagicMock()
|
||||
mock_trustgraph.return_value = mock_tg_instance
|
||||
mock_tg_instance.get_spo.return_value = None
|
||||
|
||||
# SPO query returns a list of results
|
||||
mock_result = MagicMock()
|
||||
mock_result.g = None
|
||||
mock_tg_instance.get_spo.return_value = [mock_result]
|
||||
|
||||
processor = Processor(
|
||||
taskgroup=MagicMock(),
|
||||
cassandra_username='authuser',
|
||||
|
|
@ -393,9 +399,9 @@ class TestCassandraQueryProcessor:
|
|||
query = TriplesQueryRequest(
|
||||
user='test_user',
|
||||
collection='test_collection',
|
||||
s=Value(value='test_subject', is_uri=False),
|
||||
p=Value(value='test_predicate', is_uri=False),
|
||||
o=Value(value='test_object', is_uri=False),
|
||||
s=Term(type=LITERAL, value='test_subject'),
|
||||
p=Term(type=LITERAL, value='test_predicate'),
|
||||
o=Term(type=LITERAL, value='test_object'),
|
||||
limit=100
|
||||
)
|
||||
|
||||
|
|
@ -413,27 +419,30 @@ class TestCassandraQueryProcessor:
|
|||
@patch('trustgraph.query.triples.cassandra.service.KnowledgeGraph')
|
||||
async def test_query_triples_table_reuse(self, mock_trustgraph):
|
||||
"""Test that TrustGraph is reused for same table"""
|
||||
from trustgraph.schema import TriplesQueryRequest, Value
|
||||
from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL
|
||||
|
||||
mock_tg_instance = MagicMock()
|
||||
mock_trustgraph.return_value = mock_tg_instance
|
||||
mock_tg_instance.get_spo.return_value = None
|
||||
|
||||
# SPO query returns a list of results
|
||||
mock_result = MagicMock()
|
||||
mock_result.g = None
|
||||
mock_tg_instance.get_spo.return_value = [mock_result]
|
||||
|
||||
processor = Processor(taskgroup=MagicMock())
|
||||
|
||||
|
||||
query = TriplesQueryRequest(
|
||||
user='test_user',
|
||||
collection='test_collection',
|
||||
s=Value(value='test_subject', is_uri=False),
|
||||
p=Value(value='test_predicate', is_uri=False),
|
||||
o=Value(value='test_object', is_uri=False),
|
||||
s=Term(type=LITERAL, value='test_subject'),
|
||||
p=Term(type=LITERAL, value='test_predicate'),
|
||||
o=Term(type=LITERAL, value='test_object'),
|
||||
limit=100
|
||||
)
|
||||
|
||||
|
||||
# First query should create TrustGraph
|
||||
await processor.query_triples(query)
|
||||
assert mock_trustgraph.call_count == 1
|
||||
|
||||
|
||||
# Second query with same table should reuse TrustGraph
|
||||
await processor.query_triples(query)
|
||||
assert mock_trustgraph.call_count == 1 # Should not increase
|
||||
|
|
@ -442,7 +451,7 @@ class TestCassandraQueryProcessor:
|
|||
@patch('trustgraph.query.triples.cassandra.service.KnowledgeGraph')
|
||||
async def test_query_triples_table_switching(self, mock_trustgraph):
|
||||
"""Test table switching creates new TrustGraph"""
|
||||
from trustgraph.schema import TriplesQueryRequest, Value
|
||||
from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL
|
||||
|
||||
mock_tg_instance1 = MagicMock()
|
||||
mock_tg_instance2 = MagicMock()
|
||||
|
|
@ -454,7 +463,7 @@ class TestCassandraQueryProcessor:
|
|||
query1 = TriplesQueryRequest(
|
||||
user='user1',
|
||||
collection='collection1',
|
||||
s=Value(value='test_subject', is_uri=False),
|
||||
s=Term(type=LITERAL, value='test_subject'),
|
||||
p=None,
|
||||
o=None,
|
||||
limit=100
|
||||
|
|
@ -467,7 +476,7 @@ class TestCassandraQueryProcessor:
|
|||
query2 = TriplesQueryRequest(
|
||||
user='user2',
|
||||
collection='collection2',
|
||||
s=Value(value='test_subject', is_uri=False),
|
||||
s=Term(type=LITERAL, value='test_subject'),
|
||||
p=None,
|
||||
o=None,
|
||||
limit=100
|
||||
|
|
@ -483,7 +492,7 @@ class TestCassandraQueryProcessor:
|
|||
@patch('trustgraph.query.triples.cassandra.service.KnowledgeGraph')
|
||||
async def test_query_triples_exception_handling(self, mock_trustgraph):
|
||||
"""Test exception handling during query execution"""
|
||||
from trustgraph.schema import TriplesQueryRequest, Value
|
||||
from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL
|
||||
|
||||
mock_tg_instance = MagicMock()
|
||||
mock_trustgraph.return_value = mock_tg_instance
|
||||
|
|
@ -494,9 +503,9 @@ class TestCassandraQueryProcessor:
|
|||
query = TriplesQueryRequest(
|
||||
user='test_user',
|
||||
collection='test_collection',
|
||||
s=Value(value='test_subject', is_uri=False),
|
||||
p=Value(value='test_predicate', is_uri=False),
|
||||
o=Value(value='test_object', is_uri=False),
|
||||
s=Term(type=LITERAL, value='test_subject'),
|
||||
p=Term(type=LITERAL, value='test_predicate'),
|
||||
o=Term(type=LITERAL, value='test_object'),
|
||||
limit=100
|
||||
)
|
||||
|
||||
|
|
@ -507,7 +516,7 @@ class TestCassandraQueryProcessor:
|
|||
@patch('trustgraph.query.triples.cassandra.service.KnowledgeGraph')
|
||||
async def test_query_triples_multiple_results(self, mock_trustgraph):
|
||||
"""Test query returning multiple results"""
|
||||
from trustgraph.schema import TriplesQueryRequest, Value
|
||||
from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL
|
||||
|
||||
mock_tg_instance = MagicMock()
|
||||
mock_trustgraph.return_value = mock_tg_instance
|
||||
|
|
@ -524,8 +533,8 @@ class TestCassandraQueryProcessor:
|
|||
query = TriplesQueryRequest(
|
||||
user='test_user',
|
||||
collection='test_collection',
|
||||
s=Value(value='test_subject', is_uri=False),
|
||||
p=Value(value='test_predicate', is_uri=False),
|
||||
s=Term(type=LITERAL, value='test_subject'),
|
||||
p=Term(type=LITERAL, value='test_predicate'),
|
||||
o=None,
|
||||
limit=100
|
||||
)
|
||||
|
|
@ -544,7 +553,7 @@ class TestCassandraQueryPerformanceOptimizations:
|
|||
@patch('trustgraph.query.triples.cassandra.service.KnowledgeGraph')
|
||||
async def test_get_po_query_optimization(self, mock_trustgraph):
|
||||
"""Test that get_po queries use optimized table (no ALLOW FILTERING)"""
|
||||
from trustgraph.schema import TriplesQueryRequest, Value
|
||||
from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL
|
||||
|
||||
mock_tg_instance = MagicMock()
|
||||
mock_trustgraph.return_value = mock_tg_instance
|
||||
|
|
@ -560,8 +569,8 @@ class TestCassandraQueryPerformanceOptimizations:
|
|||
user='test_user',
|
||||
collection='test_collection',
|
||||
s=None,
|
||||
p=Value(value='test_predicate', is_uri=False),
|
||||
o=Value(value='test_object', is_uri=False),
|
||||
p=Term(type=LITERAL, value='test_predicate'),
|
||||
o=Term(type=LITERAL, value='test_object'),
|
||||
limit=50
|
||||
)
|
||||
|
||||
|
|
@ -569,7 +578,7 @@ class TestCassandraQueryPerformanceOptimizations:
|
|||
|
||||
# Verify get_po was called (should use optimized po_table)
|
||||
mock_tg_instance.get_po.assert_called_once_with(
|
||||
'test_collection', 'test_predicate', 'test_object', limit=50
|
||||
'test_collection', 'test_predicate', 'test_object', g=None, limit=50
|
||||
)
|
||||
|
||||
assert len(result) == 1
|
||||
|
|
@ -581,7 +590,7 @@ class TestCassandraQueryPerformanceOptimizations:
|
|||
@patch('trustgraph.query.triples.cassandra.service.KnowledgeGraph')
|
||||
async def test_get_os_query_optimization(self, mock_trustgraph):
|
||||
"""Test that get_os queries use optimized table (no ALLOW FILTERING)"""
|
||||
from trustgraph.schema import TriplesQueryRequest, Value
|
||||
from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL
|
||||
|
||||
mock_tg_instance = MagicMock()
|
||||
mock_trustgraph.return_value = mock_tg_instance
|
||||
|
|
@ -596,9 +605,9 @@ class TestCassandraQueryPerformanceOptimizations:
|
|||
query = TriplesQueryRequest(
|
||||
user='test_user',
|
||||
collection='test_collection',
|
||||
s=Value(value='test_subject', is_uri=False),
|
||||
s=Term(type=LITERAL, value='test_subject'),
|
||||
p=None,
|
||||
o=Value(value='test_object', is_uri=False),
|
||||
o=Term(type=LITERAL, value='test_object'),
|
||||
limit=25
|
||||
)
|
||||
|
||||
|
|
@ -606,7 +615,7 @@ class TestCassandraQueryPerformanceOptimizations:
|
|||
|
||||
# Verify get_os was called (should use optimized subject_table with clustering)
|
||||
mock_tg_instance.get_os.assert_called_once_with(
|
||||
'test_collection', 'test_object', 'test_subject', limit=25
|
||||
'test_collection', 'test_object', 'test_subject', g=None, limit=25
|
||||
)
|
||||
|
||||
assert len(result) == 1
|
||||
|
|
@ -618,7 +627,7 @@ class TestCassandraQueryPerformanceOptimizations:
|
|||
@patch('trustgraph.query.triples.cassandra.service.KnowledgeGraph')
|
||||
async def test_all_query_patterns_use_correct_tables(self, mock_trustgraph):
|
||||
"""Test that all query patterns route to their optimal tables"""
|
||||
from trustgraph.schema import TriplesQueryRequest, Value
|
||||
from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL
|
||||
|
||||
mock_tg_instance = MagicMock()
|
||||
mock_trustgraph.return_value = mock_tg_instance
|
||||
|
|
@ -655,9 +664,9 @@ class TestCassandraQueryPerformanceOptimizations:
|
|||
query = TriplesQueryRequest(
|
||||
user='test_user',
|
||||
collection='test_collection',
|
||||
s=Value(value=s, is_uri=False) if s else None,
|
||||
p=Value(value=p, is_uri=False) if p else None,
|
||||
o=Value(value=o, is_uri=False) if o else None,
|
||||
s=Term(type=LITERAL, value=s) if s else None,
|
||||
p=Term(type=LITERAL, value=p) if p else None,
|
||||
o=Term(type=LITERAL, value=o) if o else None,
|
||||
limit=10
|
||||
)
|
||||
|
||||
|
|
@ -690,7 +699,7 @@ class TestCassandraQueryPerformanceOptimizations:
|
|||
@patch('trustgraph.query.triples.cassandra.service.KnowledgeGraph')
|
||||
async def test_performance_critical_po_query_no_filtering(self, mock_trustgraph):
|
||||
"""Test the performance-critical PO query that eliminates ALLOW FILTERING"""
|
||||
from trustgraph.schema import TriplesQueryRequest, Value
|
||||
from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL
|
||||
|
||||
mock_tg_instance = MagicMock()
|
||||
mock_trustgraph.return_value = mock_tg_instance
|
||||
|
|
@ -711,8 +720,8 @@ class TestCassandraQueryPerformanceOptimizations:
|
|||
user='large_dataset_user',
|
||||
collection='massive_collection',
|
||||
s=None,
|
||||
p=Value(value='http://www.w3.org/1999/02/22-rdf-syntax-ns#type', is_uri=True),
|
||||
o=Value(value='http://example.com/Person', is_uri=True),
|
||||
p=Term(type=IRI, iri='http://www.w3.org/1999/02/22-rdf-syntax-ns#type'),
|
||||
o=Term(type=IRI, iri='http://example.com/Person'),
|
||||
limit=1000
|
||||
)
|
||||
|
||||
|
|
@ -723,14 +732,15 @@ class TestCassandraQueryPerformanceOptimizations:
|
|||
'massive_collection',
|
||||
'http://www.w3.org/1999/02/22-rdf-syntax-ns#type',
|
||||
'http://example.com/Person',
|
||||
g=None,
|
||||
limit=1000
|
||||
)
|
||||
|
||||
# Verify all results were returned
|
||||
assert len(result) == 5
|
||||
for i, triple in enumerate(result):
|
||||
assert triple.s.value == f'subject_{i}'
|
||||
assert triple.p.value == 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type'
|
||||
assert triple.p.is_uri is True
|
||||
assert triple.o.value == 'http://example.com/Person'
|
||||
assert triple.o.is_uri is True
|
||||
assert triple.s.value == f'subject_{i}' # Mock returns literal values
|
||||
assert triple.p.iri == 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type'
|
||||
assert triple.p.type == IRI
|
||||
assert triple.o.iri == 'http://example.com/Person' # URIs use .iri
|
||||
assert triple.o.type == IRI
|
||||
|
|
@ -6,7 +6,7 @@ import pytest
|
|||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from trustgraph.query.triples.falkordb.service import Processor
|
||||
from trustgraph.schema import Value, TriplesQueryRequest
|
||||
from trustgraph.schema import Term, TriplesQueryRequest, IRI, LITERAL
|
||||
|
||||
|
||||
class TestFalkorDBQueryProcessor:
|
||||
|
|
@ -25,50 +25,50 @@ class TestFalkorDBQueryProcessor:
|
|||
def test_create_value_with_http_uri(self, processor):
|
||||
"""Test create_value with HTTP URI"""
|
||||
result = processor.create_value("http://example.com/resource")
|
||||
|
||||
assert isinstance(result, Value)
|
||||
assert result.value == "http://example.com/resource"
|
||||
assert result.is_uri is True
|
||||
|
||||
assert isinstance(result, Term)
|
||||
assert result.iri == "http://example.com/resource"
|
||||
assert result.type == IRI
|
||||
|
||||
def test_create_value_with_https_uri(self, processor):
|
||||
"""Test create_value with HTTPS URI"""
|
||||
result = processor.create_value("https://example.com/resource")
|
||||
|
||||
assert isinstance(result, Value)
|
||||
assert result.value == "https://example.com/resource"
|
||||
assert result.is_uri is True
|
||||
|
||||
assert isinstance(result, Term)
|
||||
assert result.iri == "https://example.com/resource"
|
||||
assert result.type == IRI
|
||||
|
||||
def test_create_value_with_literal(self, processor):
|
||||
"""Test create_value with literal value"""
|
||||
result = processor.create_value("just a literal string")
|
||||
|
||||
assert isinstance(result, Value)
|
||||
|
||||
assert isinstance(result, Term)
|
||||
assert result.value == "just a literal string"
|
||||
assert result.is_uri is False
|
||||
assert result.type == LITERAL
|
||||
|
||||
def test_create_value_with_empty_string(self, processor):
|
||||
"""Test create_value with empty string"""
|
||||
result = processor.create_value("")
|
||||
|
||||
assert isinstance(result, Value)
|
||||
|
||||
assert isinstance(result, Term)
|
||||
assert result.value == ""
|
||||
assert result.is_uri is False
|
||||
assert result.type == LITERAL
|
||||
|
||||
def test_create_value_with_partial_uri(self, processor):
|
||||
"""Test create_value with string that looks like URI but isn't complete"""
|
||||
result = processor.create_value("http")
|
||||
|
||||
assert isinstance(result, Value)
|
||||
|
||||
assert isinstance(result, Term)
|
||||
assert result.value == "http"
|
||||
assert result.is_uri is False
|
||||
assert result.type == LITERAL
|
||||
|
||||
def test_create_value_with_ftp_uri(self, processor):
|
||||
"""Test create_value with FTP URI (should not be detected as URI)"""
|
||||
result = processor.create_value("ftp://example.com/file")
|
||||
|
||||
assert isinstance(result, Value)
|
||||
|
||||
assert isinstance(result, Term)
|
||||
assert result.value == "ftp://example.com/file"
|
||||
assert result.is_uri is False
|
||||
assert result.type == LITERAL
|
||||
|
||||
@patch('trustgraph.query.triples.falkordb.service.FalkorDB')
|
||||
def test_processor_initialization_with_defaults(self, mock_falkordb):
|
||||
|
|
@ -125,9 +125,9 @@ class TestFalkorDBQueryProcessor:
|
|||
query = TriplesQueryRequest(
|
||||
user='test_user',
|
||||
collection='test_collection',
|
||||
s=Value(value="http://example.com/subject", is_uri=True),
|
||||
p=Value(value="http://example.com/predicate", is_uri=True),
|
||||
o=Value(value="literal object", is_uri=False),
|
||||
s=Term(type=IRI, iri="http://example.com/subject"),
|
||||
p=Term(type=IRI, iri="http://example.com/predicate"),
|
||||
o=Term(type=LITERAL, value="literal object"),
|
||||
limit=100
|
||||
)
|
||||
|
||||
|
|
@ -138,8 +138,8 @@ class TestFalkorDBQueryProcessor:
|
|||
|
||||
# Verify result contains the queried triple (appears twice - once from each query)
|
||||
assert len(result) == 2
|
||||
assert result[0].s.value == "http://example.com/subject"
|
||||
assert result[0].p.value == "http://example.com/predicate"
|
||||
assert result[0].s.iri == "http://example.com/subject"
|
||||
assert result[0].p.iri == "http://example.com/predicate"
|
||||
assert result[0].o.value == "literal object"
|
||||
|
||||
@patch('trustgraph.query.triples.falkordb.service.FalkorDB')
|
||||
|
|
@ -166,8 +166,8 @@ class TestFalkorDBQueryProcessor:
|
|||
query = TriplesQueryRequest(
|
||||
user='test_user',
|
||||
collection='test_collection',
|
||||
s=Value(value="http://example.com/subject", is_uri=True),
|
||||
p=Value(value="http://example.com/predicate", is_uri=True),
|
||||
s=Term(type=IRI, iri="http://example.com/subject"),
|
||||
p=Term(type=IRI, iri="http://example.com/predicate"),
|
||||
o=None,
|
||||
limit=100
|
||||
)
|
||||
|
|
@ -179,13 +179,13 @@ class TestFalkorDBQueryProcessor:
|
|||
|
||||
# Verify results contain different objects
|
||||
assert len(result) == 2
|
||||
assert result[0].s.value == "http://example.com/subject"
|
||||
assert result[0].p.value == "http://example.com/predicate"
|
||||
assert result[0].s.iri == "http://example.com/subject"
|
||||
assert result[0].p.iri == "http://example.com/predicate"
|
||||
assert result[0].o.value == "literal result"
|
||||
|
||||
assert result[1].s.value == "http://example.com/subject"
|
||||
assert result[1].p.value == "http://example.com/predicate"
|
||||
assert result[1].o.value == "http://example.com/uri_result"
|
||||
assert result[1].s.iri == "http://example.com/subject"
|
||||
assert result[1].p.iri == "http://example.com/predicate"
|
||||
assert result[1].o.iri == "http://example.com/uri_result"
|
||||
|
||||
@patch('trustgraph.query.triples.falkordb.service.FalkorDB')
|
||||
@pytest.mark.asyncio
|
||||
|
|
@ -211,9 +211,9 @@ class TestFalkorDBQueryProcessor:
|
|||
query = TriplesQueryRequest(
|
||||
user='test_user',
|
||||
collection='test_collection',
|
||||
s=Value(value="http://example.com/subject", is_uri=True),
|
||||
s=Term(type=IRI, iri="http://example.com/subject"),
|
||||
p=None,
|
||||
o=Value(value="literal object", is_uri=False),
|
||||
o=Term(type=LITERAL, value="literal object"),
|
||||
limit=100
|
||||
)
|
||||
|
||||
|
|
@ -224,12 +224,12 @@ class TestFalkorDBQueryProcessor:
|
|||
|
||||
# Verify results contain different predicates
|
||||
assert len(result) == 2
|
||||
assert result[0].s.value == "http://example.com/subject"
|
||||
assert result[0].p.value == "http://example.com/pred1"
|
||||
assert result[0].s.iri == "http://example.com/subject"
|
||||
assert result[0].p.iri == "http://example.com/pred1"
|
||||
assert result[0].o.value == "literal object"
|
||||
|
||||
assert result[1].s.value == "http://example.com/subject"
|
||||
assert result[1].p.value == "http://example.com/pred2"
|
||||
assert result[1].s.iri == "http://example.com/subject"
|
||||
assert result[1].p.iri == "http://example.com/pred2"
|
||||
assert result[1].o.value == "literal object"
|
||||
|
||||
@patch('trustgraph.query.triples.falkordb.service.FalkorDB')
|
||||
|
|
@ -256,7 +256,7 @@ class TestFalkorDBQueryProcessor:
|
|||
query = TriplesQueryRequest(
|
||||
user='test_user',
|
||||
collection='test_collection',
|
||||
s=Value(value="http://example.com/subject", is_uri=True),
|
||||
s=Term(type=IRI, iri="http://example.com/subject"),
|
||||
p=None,
|
||||
o=None,
|
||||
limit=100
|
||||
|
|
@ -269,13 +269,13 @@ class TestFalkorDBQueryProcessor:
|
|||
|
||||
# Verify results contain different predicate-object pairs
|
||||
assert len(result) == 2
|
||||
assert result[0].s.value == "http://example.com/subject"
|
||||
assert result[0].p.value == "http://example.com/pred1"
|
||||
assert result[0].s.iri == "http://example.com/subject"
|
||||
assert result[0].p.iri == "http://example.com/pred1"
|
||||
assert result[0].o.value == "literal1"
|
||||
|
||||
assert result[1].s.value == "http://example.com/subject"
|
||||
assert result[1].p.value == "http://example.com/pred2"
|
||||
assert result[1].o.value == "http://example.com/uri2"
|
||||
assert result[1].s.iri == "http://example.com/subject"
|
||||
assert result[1].p.iri == "http://example.com/pred2"
|
||||
assert result[1].o.iri == "http://example.com/uri2"
|
||||
|
||||
@patch('trustgraph.query.triples.falkordb.service.FalkorDB')
|
||||
@pytest.mark.asyncio
|
||||
|
|
@ -302,8 +302,8 @@ class TestFalkorDBQueryProcessor:
|
|||
user='test_user',
|
||||
collection='test_collection',
|
||||
s=None,
|
||||
p=Value(value="http://example.com/predicate", is_uri=True),
|
||||
o=Value(value="literal object", is_uri=False),
|
||||
p=Term(type=IRI, iri="http://example.com/predicate"),
|
||||
o=Term(type=LITERAL, value="literal object"),
|
||||
limit=100
|
||||
)
|
||||
|
||||
|
|
@ -314,12 +314,12 @@ class TestFalkorDBQueryProcessor:
|
|||
|
||||
# Verify results contain different subjects
|
||||
assert len(result) == 2
|
||||
assert result[0].s.value == "http://example.com/subj1"
|
||||
assert result[0].p.value == "http://example.com/predicate"
|
||||
assert result[0].s.iri == "http://example.com/subj1"
|
||||
assert result[0].p.iri == "http://example.com/predicate"
|
||||
assert result[0].o.value == "literal object"
|
||||
|
||||
assert result[1].s.value == "http://example.com/subj2"
|
||||
assert result[1].p.value == "http://example.com/predicate"
|
||||
assert result[1].s.iri == "http://example.com/subj2"
|
||||
assert result[1].p.iri == "http://example.com/predicate"
|
||||
assert result[1].o.value == "literal object"
|
||||
|
||||
@patch('trustgraph.query.triples.falkordb.service.FalkorDB')
|
||||
|
|
@ -347,7 +347,7 @@ class TestFalkorDBQueryProcessor:
|
|||
user='test_user',
|
||||
collection='test_collection',
|
||||
s=None,
|
||||
p=Value(value="http://example.com/predicate", is_uri=True),
|
||||
p=Term(type=IRI, iri="http://example.com/predicate"),
|
||||
o=None,
|
||||
limit=100
|
||||
)
|
||||
|
|
@ -359,13 +359,13 @@ class TestFalkorDBQueryProcessor:
|
|||
|
||||
# Verify results contain different subject-object pairs
|
||||
assert len(result) == 2
|
||||
assert result[0].s.value == "http://example.com/subj1"
|
||||
assert result[0].p.value == "http://example.com/predicate"
|
||||
assert result[0].s.iri == "http://example.com/subj1"
|
||||
assert result[0].p.iri == "http://example.com/predicate"
|
||||
assert result[0].o.value == "literal1"
|
||||
|
||||
assert result[1].s.value == "http://example.com/subj2"
|
||||
assert result[1].p.value == "http://example.com/predicate"
|
||||
assert result[1].o.value == "http://example.com/uri2"
|
||||
assert result[1].s.iri == "http://example.com/subj2"
|
||||
assert result[1].p.iri == "http://example.com/predicate"
|
||||
assert result[1].o.iri == "http://example.com/uri2"
|
||||
|
||||
@patch('trustgraph.query.triples.falkordb.service.FalkorDB')
|
||||
@pytest.mark.asyncio
|
||||
|
|
@ -393,7 +393,7 @@ class TestFalkorDBQueryProcessor:
|
|||
collection='test_collection',
|
||||
s=None,
|
||||
p=None,
|
||||
o=Value(value="literal object", is_uri=False),
|
||||
o=Term(type=LITERAL, value="literal object"),
|
||||
limit=100
|
||||
)
|
||||
|
||||
|
|
@ -404,12 +404,12 @@ class TestFalkorDBQueryProcessor:
|
|||
|
||||
# Verify results contain different subject-predicate pairs
|
||||
assert len(result) == 2
|
||||
assert result[0].s.value == "http://example.com/subj1"
|
||||
assert result[0].p.value == "http://example.com/pred1"
|
||||
assert result[0].s.iri == "http://example.com/subj1"
|
||||
assert result[0].p.iri == "http://example.com/pred1"
|
||||
assert result[0].o.value == "literal object"
|
||||
|
||||
assert result[1].s.value == "http://example.com/subj2"
|
||||
assert result[1].p.value == "http://example.com/pred2"
|
||||
assert result[1].s.iri == "http://example.com/subj2"
|
||||
assert result[1].p.iri == "http://example.com/pred2"
|
||||
assert result[1].o.value == "literal object"
|
||||
|
||||
@patch('trustgraph.query.triples.falkordb.service.FalkorDB')
|
||||
|
|
@ -449,13 +449,13 @@ class TestFalkorDBQueryProcessor:
|
|||
|
||||
# Verify results contain different triples
|
||||
assert len(result) == 2
|
||||
assert result[0].s.value == "http://example.com/s1"
|
||||
assert result[0].p.value == "http://example.com/p1"
|
||||
assert result[0].s.iri == "http://example.com/s1"
|
||||
assert result[0].p.iri == "http://example.com/p1"
|
||||
assert result[0].o.value == "literal1"
|
||||
|
||||
assert result[1].s.value == "http://example.com/s2"
|
||||
assert result[1].p.value == "http://example.com/p2"
|
||||
assert result[1].o.value == "http://example.com/o2"
|
||||
assert result[1].s.iri == "http://example.com/s2"
|
||||
assert result[1].p.iri == "http://example.com/p2"
|
||||
assert result[1].o.iri == "http://example.com/o2"
|
||||
|
||||
@patch('trustgraph.query.triples.falkordb.service.FalkorDB')
|
||||
@pytest.mark.asyncio
|
||||
|
|
@ -476,7 +476,7 @@ class TestFalkorDBQueryProcessor:
|
|||
query = TriplesQueryRequest(
|
||||
user='test_user',
|
||||
collection='test_collection',
|
||||
s=Value(value="http://example.com/subject", is_uri=True),
|
||||
s=Term(type=IRI, iri="http://example.com/subject"),
|
||||
p=None,
|
||||
o=None,
|
||||
limit=100
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import pytest
|
|||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from trustgraph.query.triples.memgraph.service import Processor
|
||||
from trustgraph.schema import Value, TriplesQueryRequest
|
||||
from trustgraph.schema import Term, TriplesQueryRequest, IRI, LITERAL
|
||||
|
||||
|
||||
class TestMemgraphQueryProcessor:
|
||||
|
|
@ -25,50 +25,50 @@ class TestMemgraphQueryProcessor:
|
|||
def test_create_value_with_http_uri(self, processor):
|
||||
"""Test create_value with HTTP URI"""
|
||||
result = processor.create_value("http://example.com/resource")
|
||||
|
||||
assert isinstance(result, Value)
|
||||
assert result.value == "http://example.com/resource"
|
||||
assert result.is_uri is True
|
||||
|
||||
assert isinstance(result, Term)
|
||||
assert result.iri == "http://example.com/resource"
|
||||
assert result.type == IRI
|
||||
|
||||
def test_create_value_with_https_uri(self, processor):
|
||||
"""Test create_value with HTTPS URI"""
|
||||
result = processor.create_value("https://example.com/resource")
|
||||
|
||||
assert isinstance(result, Value)
|
||||
assert result.value == "https://example.com/resource"
|
||||
assert result.is_uri is True
|
||||
|
||||
assert isinstance(result, Term)
|
||||
assert result.iri == "https://example.com/resource"
|
||||
assert result.type == IRI
|
||||
|
||||
def test_create_value_with_literal(self, processor):
|
||||
"""Test create_value with literal value"""
|
||||
result = processor.create_value("just a literal string")
|
||||
|
||||
assert isinstance(result, Value)
|
||||
|
||||
assert isinstance(result, Term)
|
||||
assert result.value == "just a literal string"
|
||||
assert result.is_uri is False
|
||||
assert result.type == LITERAL
|
||||
|
||||
def test_create_value_with_empty_string(self, processor):
|
||||
"""Test create_value with empty string"""
|
||||
result = processor.create_value("")
|
||||
|
||||
assert isinstance(result, Value)
|
||||
|
||||
assert isinstance(result, Term)
|
||||
assert result.value == ""
|
||||
assert result.is_uri is False
|
||||
assert result.type == LITERAL
|
||||
|
||||
def test_create_value_with_partial_uri(self, processor):
|
||||
"""Test create_value with string that looks like URI but isn't complete"""
|
||||
result = processor.create_value("http")
|
||||
|
||||
assert isinstance(result, Value)
|
||||
|
||||
assert isinstance(result, Term)
|
||||
assert result.value == "http"
|
||||
assert result.is_uri is False
|
||||
assert result.type == LITERAL
|
||||
|
||||
def test_create_value_with_ftp_uri(self, processor):
|
||||
"""Test create_value with FTP URI (should not be detected as URI)"""
|
||||
result = processor.create_value("ftp://example.com/file")
|
||||
|
||||
assert isinstance(result, Value)
|
||||
|
||||
assert isinstance(result, Term)
|
||||
assert result.value == "ftp://example.com/file"
|
||||
assert result.is_uri is False
|
||||
assert result.type == LITERAL
|
||||
|
||||
@patch('trustgraph.query.triples.memgraph.service.GraphDatabase')
|
||||
def test_processor_initialization_with_defaults(self, mock_graph_db):
|
||||
|
|
@ -124,9 +124,9 @@ class TestMemgraphQueryProcessor:
|
|||
query = TriplesQueryRequest(
|
||||
user='test_user',
|
||||
collection='test_collection',
|
||||
s=Value(value="http://example.com/subject", is_uri=True),
|
||||
p=Value(value="http://example.com/predicate", is_uri=True),
|
||||
o=Value(value="literal object", is_uri=False),
|
||||
s=Term(type=IRI, iri="http://example.com/subject"),
|
||||
p=Term(type=IRI, iri="http://example.com/predicate"),
|
||||
o=Term(type=LITERAL, value="literal object"),
|
||||
limit=100
|
||||
)
|
||||
|
||||
|
|
@ -137,8 +137,8 @@ class TestMemgraphQueryProcessor:
|
|||
|
||||
# Verify result contains the queried triple (appears twice - once from each query)
|
||||
assert len(result) == 2
|
||||
assert result[0].s.value == "http://example.com/subject"
|
||||
assert result[0].p.value == "http://example.com/predicate"
|
||||
assert result[0].s.iri == "http://example.com/subject"
|
||||
assert result[0].p.iri == "http://example.com/predicate"
|
||||
assert result[0].o.value == "literal object"
|
||||
|
||||
@patch('trustgraph.query.triples.memgraph.service.GraphDatabase')
|
||||
|
|
@ -166,8 +166,8 @@ class TestMemgraphQueryProcessor:
|
|||
query = TriplesQueryRequest(
|
||||
user='test_user',
|
||||
collection='test_collection',
|
||||
s=Value(value="http://example.com/subject", is_uri=True),
|
||||
p=Value(value="http://example.com/predicate", is_uri=True),
|
||||
s=Term(type=IRI, iri="http://example.com/subject"),
|
||||
p=Term(type=IRI, iri="http://example.com/predicate"),
|
||||
o=None,
|
||||
limit=100
|
||||
)
|
||||
|
|
@ -179,13 +179,13 @@ class TestMemgraphQueryProcessor:
|
|||
|
||||
# Verify results contain different objects
|
||||
assert len(result) == 2
|
||||
assert result[0].s.value == "http://example.com/subject"
|
||||
assert result[0].p.value == "http://example.com/predicate"
|
||||
assert result[0].s.iri == "http://example.com/subject"
|
||||
assert result[0].p.iri == "http://example.com/predicate"
|
||||
assert result[0].o.value == "literal result"
|
||||
|
||||
assert result[1].s.value == "http://example.com/subject"
|
||||
assert result[1].p.value == "http://example.com/predicate"
|
||||
assert result[1].o.value == "http://example.com/uri_result"
|
||||
assert result[1].s.iri == "http://example.com/subject"
|
||||
assert result[1].p.iri == "http://example.com/predicate"
|
||||
assert result[1].o.iri == "http://example.com/uri_result"
|
||||
|
||||
@patch('trustgraph.query.triples.memgraph.service.GraphDatabase')
|
||||
@pytest.mark.asyncio
|
||||
|
|
@ -212,9 +212,9 @@ class TestMemgraphQueryProcessor:
|
|||
query = TriplesQueryRequest(
|
||||
user='test_user',
|
||||
collection='test_collection',
|
||||
s=Value(value="http://example.com/subject", is_uri=True),
|
||||
s=Term(type=IRI, iri="http://example.com/subject"),
|
||||
p=None,
|
||||
o=Value(value="literal object", is_uri=False),
|
||||
o=Term(type=LITERAL, value="literal object"),
|
||||
limit=100
|
||||
)
|
||||
|
||||
|
|
@ -225,12 +225,12 @@ class TestMemgraphQueryProcessor:
|
|||
|
||||
# Verify results contain different predicates
|
||||
assert len(result) == 2
|
||||
assert result[0].s.value == "http://example.com/subject"
|
||||
assert result[0].p.value == "http://example.com/pred1"
|
||||
assert result[0].s.iri == "http://example.com/subject"
|
||||
assert result[0].p.iri == "http://example.com/pred1"
|
||||
assert result[0].o.value == "literal object"
|
||||
|
||||
assert result[1].s.value == "http://example.com/subject"
|
||||
assert result[1].p.value == "http://example.com/pred2"
|
||||
assert result[1].s.iri == "http://example.com/subject"
|
||||
assert result[1].p.iri == "http://example.com/pred2"
|
||||
assert result[1].o.value == "literal object"
|
||||
|
||||
@patch('trustgraph.query.triples.memgraph.service.GraphDatabase')
|
||||
|
|
@ -258,7 +258,7 @@ class TestMemgraphQueryProcessor:
|
|||
query = TriplesQueryRequest(
|
||||
user='test_user',
|
||||
collection='test_collection',
|
||||
s=Value(value="http://example.com/subject", is_uri=True),
|
||||
s=Term(type=IRI, iri="http://example.com/subject"),
|
||||
p=None,
|
||||
o=None,
|
||||
limit=100
|
||||
|
|
@ -271,13 +271,13 @@ class TestMemgraphQueryProcessor:
|
|||
|
||||
# Verify results contain different predicate-object pairs
|
||||
assert len(result) == 2
|
||||
assert result[0].s.value == "http://example.com/subject"
|
||||
assert result[0].p.value == "http://example.com/pred1"
|
||||
assert result[0].s.iri == "http://example.com/subject"
|
||||
assert result[0].p.iri == "http://example.com/pred1"
|
||||
assert result[0].o.value == "literal1"
|
||||
|
||||
assert result[1].s.value == "http://example.com/subject"
|
||||
assert result[1].p.value == "http://example.com/pred2"
|
||||
assert result[1].o.value == "http://example.com/uri2"
|
||||
assert result[1].s.iri == "http://example.com/subject"
|
||||
assert result[1].p.iri == "http://example.com/pred2"
|
||||
assert result[1].o.iri == "http://example.com/uri2"
|
||||
|
||||
@patch('trustgraph.query.triples.memgraph.service.GraphDatabase')
|
||||
@pytest.mark.asyncio
|
||||
|
|
@ -305,8 +305,8 @@ class TestMemgraphQueryProcessor:
|
|||
user='test_user',
|
||||
collection='test_collection',
|
||||
s=None,
|
||||
p=Value(value="http://example.com/predicate", is_uri=True),
|
||||
o=Value(value="literal object", is_uri=False),
|
||||
p=Term(type=IRI, iri="http://example.com/predicate"),
|
||||
o=Term(type=LITERAL, value="literal object"),
|
||||
limit=100
|
||||
)
|
||||
|
||||
|
|
@ -317,12 +317,12 @@ class TestMemgraphQueryProcessor:
|
|||
|
||||
# Verify results contain different subjects
|
||||
assert len(result) == 2
|
||||
assert result[0].s.value == "http://example.com/subj1"
|
||||
assert result[0].p.value == "http://example.com/predicate"
|
||||
assert result[0].s.iri == "http://example.com/subj1"
|
||||
assert result[0].p.iri == "http://example.com/predicate"
|
||||
assert result[0].o.value == "literal object"
|
||||
|
||||
assert result[1].s.value == "http://example.com/subj2"
|
||||
assert result[1].p.value == "http://example.com/predicate"
|
||||
assert result[1].s.iri == "http://example.com/subj2"
|
||||
assert result[1].p.iri == "http://example.com/predicate"
|
||||
assert result[1].o.value == "literal object"
|
||||
|
||||
@patch('trustgraph.query.triples.memgraph.service.GraphDatabase')
|
||||
|
|
@ -351,7 +351,7 @@ class TestMemgraphQueryProcessor:
|
|||
user='test_user',
|
||||
collection='test_collection',
|
||||
s=None,
|
||||
p=Value(value="http://example.com/predicate", is_uri=True),
|
||||
p=Term(type=IRI, iri="http://example.com/predicate"),
|
||||
o=None,
|
||||
limit=100
|
||||
)
|
||||
|
|
@ -363,13 +363,13 @@ class TestMemgraphQueryProcessor:
|
|||
|
||||
# Verify results contain different subject-object pairs
|
||||
assert len(result) == 2
|
||||
assert result[0].s.value == "http://example.com/subj1"
|
||||
assert result[0].p.value == "http://example.com/predicate"
|
||||
assert result[0].s.iri == "http://example.com/subj1"
|
||||
assert result[0].p.iri == "http://example.com/predicate"
|
||||
assert result[0].o.value == "literal1"
|
||||
|
||||
assert result[1].s.value == "http://example.com/subj2"
|
||||
assert result[1].p.value == "http://example.com/predicate"
|
||||
assert result[1].o.value == "http://example.com/uri2"
|
||||
assert result[1].s.iri == "http://example.com/subj2"
|
||||
assert result[1].p.iri == "http://example.com/predicate"
|
||||
assert result[1].o.iri == "http://example.com/uri2"
|
||||
|
||||
@patch('trustgraph.query.triples.memgraph.service.GraphDatabase')
|
||||
@pytest.mark.asyncio
|
||||
|
|
@ -398,7 +398,7 @@ class TestMemgraphQueryProcessor:
|
|||
collection='test_collection',
|
||||
s=None,
|
||||
p=None,
|
||||
o=Value(value="literal object", is_uri=False),
|
||||
o=Term(type=LITERAL, value="literal object"),
|
||||
limit=100
|
||||
)
|
||||
|
||||
|
|
@ -409,12 +409,12 @@ class TestMemgraphQueryProcessor:
|
|||
|
||||
# Verify results contain different subject-predicate pairs
|
||||
assert len(result) == 2
|
||||
assert result[0].s.value == "http://example.com/subj1"
|
||||
assert result[0].p.value == "http://example.com/pred1"
|
||||
assert result[0].s.iri == "http://example.com/subj1"
|
||||
assert result[0].p.iri == "http://example.com/pred1"
|
||||
assert result[0].o.value == "literal object"
|
||||
|
||||
assert result[1].s.value == "http://example.com/subj2"
|
||||
assert result[1].p.value == "http://example.com/pred2"
|
||||
assert result[1].s.iri == "http://example.com/subj2"
|
||||
assert result[1].p.iri == "http://example.com/pred2"
|
||||
assert result[1].o.value == "literal object"
|
||||
|
||||
@patch('trustgraph.query.triples.memgraph.service.GraphDatabase')
|
||||
|
|
@ -455,13 +455,13 @@ class TestMemgraphQueryProcessor:
|
|||
|
||||
# Verify results contain different triples
|
||||
assert len(result) == 2
|
||||
assert result[0].s.value == "http://example.com/s1"
|
||||
assert result[0].p.value == "http://example.com/p1"
|
||||
assert result[0].s.iri == "http://example.com/s1"
|
||||
assert result[0].p.iri == "http://example.com/p1"
|
||||
assert result[0].o.value == "literal1"
|
||||
|
||||
assert result[1].s.value == "http://example.com/s2"
|
||||
assert result[1].p.value == "http://example.com/p2"
|
||||
assert result[1].o.value == "http://example.com/o2"
|
||||
assert result[1].s.iri == "http://example.com/s2"
|
||||
assert result[1].p.iri == "http://example.com/p2"
|
||||
assert result[1].o.iri == "http://example.com/o2"
|
||||
|
||||
@patch('trustgraph.query.triples.memgraph.service.GraphDatabase')
|
||||
@pytest.mark.asyncio
|
||||
|
|
@ -480,7 +480,7 @@ class TestMemgraphQueryProcessor:
|
|||
query = TriplesQueryRequest(
|
||||
user='test_user',
|
||||
collection='test_collection',
|
||||
s=Value(value="http://example.com/subject", is_uri=True),
|
||||
s=Term(type=IRI, iri="http://example.com/subject"),
|
||||
p=None,
|
||||
o=None,
|
||||
limit=100
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import pytest
|
|||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from trustgraph.query.triples.neo4j.service import Processor
|
||||
from trustgraph.schema import Value, TriplesQueryRequest
|
||||
from trustgraph.schema import Term, TriplesQueryRequest, IRI, LITERAL
|
||||
|
||||
|
||||
class TestNeo4jQueryProcessor:
|
||||
|
|
@ -25,50 +25,50 @@ class TestNeo4jQueryProcessor:
|
|||
def test_create_value_with_http_uri(self, processor):
|
||||
"""Test create_value with HTTP URI"""
|
||||
result = processor.create_value("http://example.com/resource")
|
||||
|
||||
assert isinstance(result, Value)
|
||||
assert result.value == "http://example.com/resource"
|
||||
assert result.is_uri is True
|
||||
|
||||
assert isinstance(result, Term)
|
||||
assert result.iri == "http://example.com/resource"
|
||||
assert result.type == IRI
|
||||
|
||||
def test_create_value_with_https_uri(self, processor):
|
||||
"""Test create_value with HTTPS URI"""
|
||||
result = processor.create_value("https://example.com/resource")
|
||||
|
||||
assert isinstance(result, Value)
|
||||
assert result.value == "https://example.com/resource"
|
||||
assert result.is_uri is True
|
||||
|
||||
assert isinstance(result, Term)
|
||||
assert result.iri == "https://example.com/resource"
|
||||
assert result.type == IRI
|
||||
|
||||
def test_create_value_with_literal(self, processor):
|
||||
"""Test create_value with literal value"""
|
||||
result = processor.create_value("just a literal string")
|
||||
|
||||
assert isinstance(result, Value)
|
||||
|
||||
assert isinstance(result, Term)
|
||||
assert result.value == "just a literal string"
|
||||
assert result.is_uri is False
|
||||
assert result.type == LITERAL
|
||||
|
||||
def test_create_value_with_empty_string(self, processor):
|
||||
"""Test create_value with empty string"""
|
||||
result = processor.create_value("")
|
||||
|
||||
assert isinstance(result, Value)
|
||||
|
||||
assert isinstance(result, Term)
|
||||
assert result.value == ""
|
||||
assert result.is_uri is False
|
||||
assert result.type == LITERAL
|
||||
|
||||
def test_create_value_with_partial_uri(self, processor):
|
||||
"""Test create_value with string that looks like URI but isn't complete"""
|
||||
result = processor.create_value("http")
|
||||
|
||||
assert isinstance(result, Value)
|
||||
|
||||
assert isinstance(result, Term)
|
||||
assert result.value == "http"
|
||||
assert result.is_uri is False
|
||||
assert result.type == LITERAL
|
||||
|
||||
def test_create_value_with_ftp_uri(self, processor):
|
||||
"""Test create_value with FTP URI (should not be detected as URI)"""
|
||||
result = processor.create_value("ftp://example.com/file")
|
||||
|
||||
assert isinstance(result, Value)
|
||||
|
||||
assert isinstance(result, Term)
|
||||
assert result.value == "ftp://example.com/file"
|
||||
assert result.is_uri is False
|
||||
assert result.type == LITERAL
|
||||
|
||||
@patch('trustgraph.query.triples.neo4j.service.GraphDatabase')
|
||||
def test_processor_initialization_with_defaults(self, mock_graph_db):
|
||||
|
|
@ -124,9 +124,9 @@ class TestNeo4jQueryProcessor:
|
|||
query = TriplesQueryRequest(
|
||||
user='test_user',
|
||||
collection='test_collection',
|
||||
s=Value(value="http://example.com/subject", is_uri=True),
|
||||
p=Value(value="http://example.com/predicate", is_uri=True),
|
||||
o=Value(value="literal object", is_uri=False),
|
||||
s=Term(type=IRI, iri="http://example.com/subject"),
|
||||
p=Term(type=IRI, iri="http://example.com/predicate"),
|
||||
o=Term(type=LITERAL, value="literal object"),
|
||||
limit=100
|
||||
)
|
||||
|
||||
|
|
@ -137,8 +137,8 @@ class TestNeo4jQueryProcessor:
|
|||
|
||||
# Verify result contains the queried triple (appears twice - once from each query)
|
||||
assert len(result) == 2
|
||||
assert result[0].s.value == "http://example.com/subject"
|
||||
assert result[0].p.value == "http://example.com/predicate"
|
||||
assert result[0].s.iri == "http://example.com/subject"
|
||||
assert result[0].p.iri == "http://example.com/predicate"
|
||||
assert result[0].o.value == "literal object"
|
||||
|
||||
@patch('trustgraph.query.triples.neo4j.service.GraphDatabase')
|
||||
|
|
@ -166,8 +166,8 @@ class TestNeo4jQueryProcessor:
|
|||
query = TriplesQueryRequest(
|
||||
user='test_user',
|
||||
collection='test_collection',
|
||||
s=Value(value="http://example.com/subject", is_uri=True),
|
||||
p=Value(value="http://example.com/predicate", is_uri=True),
|
||||
s=Term(type=IRI, iri="http://example.com/subject"),
|
||||
p=Term(type=IRI, iri="http://example.com/predicate"),
|
||||
o=None,
|
||||
limit=100
|
||||
)
|
||||
|
|
@ -179,13 +179,13 @@ class TestNeo4jQueryProcessor:
|
|||
|
||||
# Verify results contain different objects
|
||||
assert len(result) == 2
|
||||
assert result[0].s.value == "http://example.com/subject"
|
||||
assert result[0].p.value == "http://example.com/predicate"
|
||||
assert result[0].s.iri == "http://example.com/subject"
|
||||
assert result[0].p.iri == "http://example.com/predicate"
|
||||
assert result[0].o.value == "literal result"
|
||||
|
||||
assert result[1].s.value == "http://example.com/subject"
|
||||
assert result[1].p.value == "http://example.com/predicate"
|
||||
assert result[1].o.value == "http://example.com/uri_result"
|
||||
|
||||
assert result[1].s.iri == "http://example.com/subject"
|
||||
assert result[1].p.iri == "http://example.com/predicate"
|
||||
assert result[1].o.iri == "http://example.com/uri_result"
|
||||
|
||||
@patch('trustgraph.query.triples.neo4j.service.GraphDatabase')
|
||||
@pytest.mark.asyncio
|
||||
|
|
@ -225,13 +225,13 @@ class TestNeo4jQueryProcessor:
|
|||
|
||||
# Verify results contain different triples
|
||||
assert len(result) == 2
|
||||
assert result[0].s.value == "http://example.com/s1"
|
||||
assert result[0].p.value == "http://example.com/p1"
|
||||
assert result[0].s.iri == "http://example.com/s1"
|
||||
assert result[0].p.iri == "http://example.com/p1"
|
||||
assert result[0].o.value == "literal1"
|
||||
|
||||
assert result[1].s.value == "http://example.com/s2"
|
||||
assert result[1].p.value == "http://example.com/p2"
|
||||
assert result[1].o.value == "http://example.com/o2"
|
||||
|
||||
assert result[1].s.iri == "http://example.com/s2"
|
||||
assert result[1].p.iri == "http://example.com/p2"
|
||||
assert result[1].o.iri == "http://example.com/o2"
|
||||
|
||||
@patch('trustgraph.query.triples.neo4j.service.GraphDatabase')
|
||||
@pytest.mark.asyncio
|
||||
|
|
@ -250,12 +250,12 @@ class TestNeo4jQueryProcessor:
|
|||
query = TriplesQueryRequest(
|
||||
user='test_user',
|
||||
collection='test_collection',
|
||||
s=Value(value="http://example.com/subject", is_uri=True),
|
||||
s=Term(type=IRI, iri="http://example.com/subject"),
|
||||
p=None,
|
||||
o=None,
|
||||
limit=100
|
||||
)
|
||||
|
||||
|
||||
# Should raise the exception
|
||||
with pytest.raises(Exception, match="Database connection failed"):
|
||||
await processor.query_triples(query)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import pytest
|
|||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from trustgraph.storage.graph_embeddings.milvus.write import Processor
|
||||
from trustgraph.schema import Value, EntityEmbeddings
|
||||
from trustgraph.schema import Term, EntityEmbeddings, IRI, LITERAL
|
||||
|
||||
|
||||
class TestMilvusGraphEmbeddingsStorageProcessor:
|
||||
|
|
@ -22,11 +22,11 @@ class TestMilvusGraphEmbeddingsStorageProcessor:
|
|||
|
||||
# Create test entities with embeddings
|
||||
entity1 = EntityEmbeddings(
|
||||
entity=Value(value='http://example.com/entity1', is_uri=True),
|
||||
entity=Term(type=IRI, iri='http://example.com/entity1'),
|
||||
vectors=[[0.1, 0.2, 0.3], [0.4, 0.5, 0.6]]
|
||||
)
|
||||
entity2 = EntityEmbeddings(
|
||||
entity=Value(value='literal entity', is_uri=False),
|
||||
entity=Term(type=LITERAL, value='literal entity'),
|
||||
vectors=[[0.7, 0.8, 0.9]]
|
||||
)
|
||||
message.entities = [entity1, entity2]
|
||||
|
|
@ -84,7 +84,7 @@ class TestMilvusGraphEmbeddingsStorageProcessor:
|
|||
message.metadata.collection = 'test_collection'
|
||||
|
||||
entity = EntityEmbeddings(
|
||||
entity=Value(value='http://example.com/entity', is_uri=True),
|
||||
entity=Term(type=IRI, iri='http://example.com/entity'),
|
||||
vectors=[[0.1, 0.2, 0.3], [0.4, 0.5, 0.6]]
|
||||
)
|
||||
message.entities = [entity]
|
||||
|
|
@ -136,7 +136,7 @@ class TestMilvusGraphEmbeddingsStorageProcessor:
|
|||
message.metadata.collection = 'test_collection'
|
||||
|
||||
entity = EntityEmbeddings(
|
||||
entity=Value(value='', is_uri=False),
|
||||
entity=Term(type=LITERAL, value=''),
|
||||
vectors=[[0.1, 0.2, 0.3]]
|
||||
)
|
||||
message.entities = [entity]
|
||||
|
|
@ -155,7 +155,7 @@ class TestMilvusGraphEmbeddingsStorageProcessor:
|
|||
message.metadata.collection = 'test_collection'
|
||||
|
||||
entity = EntityEmbeddings(
|
||||
entity=Value(value=None, is_uri=False),
|
||||
entity=Term(type=LITERAL, value=None),
|
||||
vectors=[[0.1, 0.2, 0.3]]
|
||||
)
|
||||
message.entities = [entity]
|
||||
|
|
@ -174,15 +174,15 @@ class TestMilvusGraphEmbeddingsStorageProcessor:
|
|||
message.metadata.collection = 'test_collection'
|
||||
|
||||
valid_entity = EntityEmbeddings(
|
||||
entity=Value(value='http://example.com/valid', is_uri=True),
|
||||
entity=Term(type=IRI, iri='http://example.com/valid'),
|
||||
vectors=[[0.1, 0.2, 0.3]]
|
||||
)
|
||||
empty_entity = EntityEmbeddings(
|
||||
entity=Value(value='', is_uri=False),
|
||||
entity=Term(type=LITERAL, value=''),
|
||||
vectors=[[0.4, 0.5, 0.6]]
|
||||
)
|
||||
none_entity = EntityEmbeddings(
|
||||
entity=Value(value=None, is_uri=False),
|
||||
entity=Term(type=LITERAL, value=None),
|
||||
vectors=[[0.7, 0.8, 0.9]]
|
||||
)
|
||||
message.entities = [valid_entity, empty_entity, none_entity]
|
||||
|
|
@ -217,7 +217,7 @@ class TestMilvusGraphEmbeddingsStorageProcessor:
|
|||
message.metadata.collection = 'test_collection'
|
||||
|
||||
entity = EntityEmbeddings(
|
||||
entity=Value(value='http://example.com/entity', is_uri=True),
|
||||
entity=Term(type=IRI, iri='http://example.com/entity'),
|
||||
vectors=[]
|
||||
)
|
||||
message.entities = [entity]
|
||||
|
|
@ -236,7 +236,7 @@ class TestMilvusGraphEmbeddingsStorageProcessor:
|
|||
message.metadata.collection = 'test_collection'
|
||||
|
||||
entity = EntityEmbeddings(
|
||||
entity=Value(value='http://example.com/entity', is_uri=True),
|
||||
entity=Term(type=IRI, iri='http://example.com/entity'),
|
||||
vectors=[
|
||||
[0.1, 0.2], # 2D vector
|
||||
[0.3, 0.4, 0.5, 0.6], # 4D vector
|
||||
|
|
@ -269,11 +269,11 @@ class TestMilvusGraphEmbeddingsStorageProcessor:
|
|||
message.metadata.collection = 'test_collection'
|
||||
|
||||
uri_entity = EntityEmbeddings(
|
||||
entity=Value(value='http://example.com/uri_entity', is_uri=True),
|
||||
entity=Term(type=IRI, iri='http://example.com/uri_entity'),
|
||||
vectors=[[0.1, 0.2, 0.3]]
|
||||
)
|
||||
literal_entity = EntityEmbeddings(
|
||||
entity=Value(value='literal entity text', is_uri=False),
|
||||
entity=Term(type=LITERAL, value='literal entity text'),
|
||||
vectors=[[0.4, 0.5, 0.6]]
|
||||
)
|
||||
message.entities = [uri_entity, literal_entity]
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ from unittest import IsolatedAsyncioTestCase
|
|||
|
||||
# Import the service under test
|
||||
from trustgraph.storage.graph_embeddings.qdrant.write import Processor
|
||||
from trustgraph.schema import IRI, LITERAL
|
||||
|
||||
|
||||
class TestQdrantGraphEmbeddingsStorage(IsolatedAsyncioTestCase):
|
||||
|
|
@ -67,7 +68,8 @@ class TestQdrantGraphEmbeddingsStorage(IsolatedAsyncioTestCase):
|
|||
mock_message.metadata.collection = 'test_collection'
|
||||
|
||||
mock_entity = MagicMock()
|
||||
mock_entity.entity.value = 'test_entity'
|
||||
mock_entity.entity.type = IRI
|
||||
mock_entity.entity.iri = 'test_entity'
|
||||
mock_entity.vectors = [[0.1, 0.2, 0.3]] # Single vector with 3 dimensions
|
||||
|
||||
mock_message.entities = [mock_entity]
|
||||
|
|
@ -120,11 +122,13 @@ class TestQdrantGraphEmbeddingsStorage(IsolatedAsyncioTestCase):
|
|||
mock_message.metadata.collection = 'multi_collection'
|
||||
|
||||
mock_entity1 = MagicMock()
|
||||
mock_entity1.entity.value = 'entity_one'
|
||||
mock_entity1.entity.type = IRI
|
||||
mock_entity1.entity.iri = 'entity_one'
|
||||
mock_entity1.vectors = [[0.1, 0.2]]
|
||||
|
||||
|
||||
mock_entity2 = MagicMock()
|
||||
mock_entity2.entity.value = 'entity_two'
|
||||
mock_entity2.entity.type = IRI
|
||||
mock_entity2.entity.iri = 'entity_two'
|
||||
mock_entity2.vectors = [[0.3, 0.4]]
|
||||
|
||||
mock_message.entities = [mock_entity1, mock_entity2]
|
||||
|
|
@ -179,7 +183,8 @@ class TestQdrantGraphEmbeddingsStorage(IsolatedAsyncioTestCase):
|
|||
mock_message.metadata.collection = 'vector_collection'
|
||||
|
||||
mock_entity = MagicMock()
|
||||
mock_entity.entity.value = 'multi_vector_entity'
|
||||
mock_entity.entity.type = IRI
|
||||
mock_entity.entity.iri = 'multi_vector_entity'
|
||||
mock_entity.vectors = [
|
||||
[0.1, 0.2, 0.3],
|
||||
[0.4, 0.5, 0.6],
|
||||
|
|
@ -231,11 +236,12 @@ class TestQdrantGraphEmbeddingsStorage(IsolatedAsyncioTestCase):
|
|||
mock_message.metadata.collection = 'empty_collection'
|
||||
|
||||
mock_entity_empty = MagicMock()
|
||||
mock_entity_empty.entity.type = LITERAL
|
||||
mock_entity_empty.entity.value = "" # Empty string
|
||||
mock_entity_empty.vectors = [[0.1, 0.2]]
|
||||
|
||||
|
||||
mock_entity_none = MagicMock()
|
||||
mock_entity_none.entity.value = None # None value
|
||||
mock_entity_none.entity = None # None entity
|
||||
mock_entity_none.vectors = [[0.3, 0.4]]
|
||||
|
||||
mock_message.entities = [mock_entity_empty, mock_entity_none]
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from unittest.mock import MagicMock, patch, call
|
|||
|
||||
from trustgraph.storage.triples.neo4j.write import Processor as StorageProcessor
|
||||
from trustgraph.query.triples.neo4j.service import Processor as QueryProcessor
|
||||
from trustgraph.schema import Triples, Triple, Value, Metadata
|
||||
from trustgraph.schema import Triples, Triple, Term, Metadata, IRI, LITERAL
|
||||
from trustgraph.schema import TriplesQueryRequest
|
||||
|
||||
|
||||
|
|
@ -60,9 +60,9 @@ class TestNeo4jUserCollectionIsolation:
|
|||
)
|
||||
|
||||
triple = Triple(
|
||||
s=Value(value="http://example.com/subject", is_uri=True),
|
||||
p=Value(value="http://example.com/predicate", is_uri=True),
|
||||
o=Value(value="literal_value", is_uri=False)
|
||||
s=Term(type=IRI, iri="http://example.com/subject"),
|
||||
p=Term(type=IRI, iri="http://example.com/predicate"),
|
||||
o=Term(type=LITERAL, value="literal_value")
|
||||
)
|
||||
|
||||
message = Triples(
|
||||
|
|
@ -128,9 +128,9 @@ class TestNeo4jUserCollectionIsolation:
|
|||
metadata = Metadata(id="test-id")
|
||||
|
||||
triple = Triple(
|
||||
s=Value(value="http://example.com/subject", is_uri=True),
|
||||
p=Value(value="http://example.com/predicate", is_uri=True),
|
||||
o=Value(value="http://example.com/object", is_uri=True)
|
||||
s=Term(type=IRI, iri="http://example.com/subject"),
|
||||
p=Term(type=IRI, iri="http://example.com/predicate"),
|
||||
o=Term(type=IRI, iri="http://example.com/object")
|
||||
)
|
||||
|
||||
message = Triples(
|
||||
|
|
@ -170,8 +170,8 @@ class TestNeo4jUserCollectionIsolation:
|
|||
query = TriplesQueryRequest(
|
||||
user="test_user",
|
||||
collection="test_collection",
|
||||
s=Value(value="http://example.com/subject", is_uri=True),
|
||||
p=Value(value="http://example.com/predicate", is_uri=True),
|
||||
s=Term(type=IRI, iri="http://example.com/subject"),
|
||||
p=Term(type=IRI, iri="http://example.com/predicate"),
|
||||
o=None
|
||||
)
|
||||
|
||||
|
|
@ -254,9 +254,9 @@ class TestNeo4jUserCollectionIsolation:
|
|||
metadata=Metadata(user="user1", collection="coll1"),
|
||||
triples=[
|
||||
Triple(
|
||||
s=Value(value="http://example.com/user1/subject", is_uri=True),
|
||||
p=Value(value="http://example.com/predicate", is_uri=True),
|
||||
o=Value(value="user1_data", is_uri=False)
|
||||
s=Term(type=IRI, iri="http://example.com/user1/subject"),
|
||||
p=Term(type=IRI, iri="http://example.com/predicate"),
|
||||
o=Term(type=LITERAL, value="user1_data")
|
||||
)
|
||||
]
|
||||
)
|
||||
|
|
@ -265,9 +265,9 @@ class TestNeo4jUserCollectionIsolation:
|
|||
metadata=Metadata(user="user2", collection="coll2"),
|
||||
triples=[
|
||||
Triple(
|
||||
s=Value(value="http://example.com/user2/subject", is_uri=True),
|
||||
p=Value(value="http://example.com/predicate", is_uri=True),
|
||||
o=Value(value="user2_data", is_uri=False)
|
||||
s=Term(type=IRI, iri="http://example.com/user2/subject"),
|
||||
p=Term(type=IRI, iri="http://example.com/predicate"),
|
||||
o=Term(type=LITERAL, value="user2_data")
|
||||
)
|
||||
]
|
||||
)
|
||||
|
|
@ -429,9 +429,9 @@ class TestNeo4jUserCollectionRegression:
|
|||
metadata=Metadata(user="user1", collection="coll1"),
|
||||
triples=[
|
||||
Triple(
|
||||
s=Value(value=shared_uri, is_uri=True),
|
||||
p=Value(value="http://example.com/p", is_uri=True),
|
||||
o=Value(value="user1_value", is_uri=False)
|
||||
s=Term(type=IRI, iri=shared_uri),
|
||||
p=Term(type=IRI, iri="http://example.com/p"),
|
||||
o=Term(type=LITERAL, value="user1_value")
|
||||
)
|
||||
]
|
||||
)
|
||||
|
|
@ -440,9 +440,9 @@ class TestNeo4jUserCollectionRegression:
|
|||
metadata=Metadata(user="user2", collection="coll2"),
|
||||
triples=[
|
||||
Triple(
|
||||
s=Value(value=shared_uri, is_uri=True),
|
||||
p=Value(value="http://example.com/p", is_uri=True),
|
||||
o=Value(value="user2_value", is_uri=False)
|
||||
s=Term(type=IRI, iri=shared_uri),
|
||||
p=Term(type=IRI, iri="http://example.com/p"),
|
||||
o=Term(type=LITERAL, value="user2_value")
|
||||
)
|
||||
]
|
||||
)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@ import pytest
|
|||
from unittest.mock import MagicMock, patch, AsyncMock
|
||||
|
||||
from trustgraph.storage.triples.cassandra.write import Processor
|
||||
from trustgraph.schema import Value, Triple
|
||||
from trustgraph.schema import Triple, LITERAL
|
||||
from trustgraph.direct.cassandra_kg import DEFAULT_GRAPH
|
||||
|
||||
|
||||
class TestCassandraStorageProcessor:
|
||||
|
|
@ -175,29 +176,37 @@ class TestCassandraStorageProcessor:
|
|||
|
||||
processor = Processor(taskgroup=taskgroup_mock)
|
||||
|
||||
# Create mock triples
|
||||
# Create mock triples with proper Term structure
|
||||
triple1 = MagicMock()
|
||||
triple1.s.type = LITERAL
|
||||
triple1.s.value = 'subject1'
|
||||
triple1.p.type = LITERAL
|
||||
triple1.p.value = 'predicate1'
|
||||
triple1.o.type = LITERAL
|
||||
triple1.o.value = 'object1'
|
||||
|
||||
triple1.g = None
|
||||
|
||||
triple2 = MagicMock()
|
||||
triple2.s.type = LITERAL
|
||||
triple2.s.value = 'subject2'
|
||||
triple2.p.type = LITERAL
|
||||
triple2.p.value = 'predicate2'
|
||||
triple2.o.type = LITERAL
|
||||
triple2.o.value = 'object2'
|
||||
|
||||
triple2.g = None
|
||||
|
||||
# Create mock message
|
||||
mock_message = MagicMock()
|
||||
mock_message.metadata.user = 'user1'
|
||||
mock_message.metadata.collection = 'collection1'
|
||||
mock_message.triples = [triple1, triple2]
|
||||
|
||||
|
||||
await processor.store_triples(mock_message)
|
||||
|
||||
# Verify both triples were inserted
|
||||
|
||||
# Verify both triples were inserted (with g= parameter)
|
||||
assert mock_tg_instance.insert.call_count == 2
|
||||
mock_tg_instance.insert.assert_any_call('collection1', 'subject1', 'predicate1', 'object1')
|
||||
mock_tg_instance.insert.assert_any_call('collection1', 'subject2', 'predicate2', 'object2')
|
||||
mock_tg_instance.insert.assert_any_call('collection1', 'subject1', 'predicate1', 'object1', g=DEFAULT_GRAPH)
|
||||
mock_tg_instance.insert.assert_any_call('collection1', 'subject2', 'predicate2', 'object2', g=DEFAULT_GRAPH)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch('trustgraph.storage.triples.cassandra.write.KnowledgeGraph')
|
||||
|
|
@ -369,25 +378,30 @@ class TestCassandraStorageProcessor:
|
|||
|
||||
processor = Processor(taskgroup=taskgroup_mock)
|
||||
|
||||
# Create triple with special characters
|
||||
# Create triple with special characters and proper Term structure
|
||||
triple = MagicMock()
|
||||
triple.s.type = LITERAL
|
||||
triple.s.value = 'subject with spaces & symbols'
|
||||
triple.p.type = LITERAL
|
||||
triple.p.value = 'predicate:with/colons'
|
||||
triple.o.type = LITERAL
|
||||
triple.o.value = 'object with "quotes" and unicode: ñáéíóú'
|
||||
|
||||
triple.g = None
|
||||
|
||||
mock_message = MagicMock()
|
||||
mock_message.metadata.user = 'test_user'
|
||||
mock_message.metadata.collection = 'test_collection'
|
||||
mock_message.triples = [triple]
|
||||
|
||||
|
||||
await processor.store_triples(mock_message)
|
||||
|
||||
|
||||
# Verify the triple was inserted with special characters preserved
|
||||
mock_tg_instance.insert.assert_called_once_with(
|
||||
'test_collection',
|
||||
'subject with spaces & symbols',
|
||||
'predicate:with/colons',
|
||||
'object with "quotes" and unicode: ñáéíóú'
|
||||
'object with "quotes" and unicode: ñáéíóú',
|
||||
g=DEFAULT_GRAPH
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
@ -475,11 +489,15 @@ class TestCassandraPerformanceOptimizations:
|
|||
|
||||
processor = Processor(taskgroup=taskgroup_mock)
|
||||
|
||||
# Create test triple
|
||||
# Create test triple with proper Term structure
|
||||
triple = MagicMock()
|
||||
triple.s.type = LITERAL
|
||||
triple.s.value = 'test_subject'
|
||||
triple.p.type = LITERAL
|
||||
triple.p.value = 'test_predicate'
|
||||
triple.o.type = LITERAL
|
||||
triple.o.value = 'test_object'
|
||||
triple.g = None
|
||||
|
||||
mock_message = MagicMock()
|
||||
mock_message.metadata.user = 'user1'
|
||||
|
|
@ -490,7 +508,8 @@ class TestCassandraPerformanceOptimizations:
|
|||
|
||||
# Verify insert was called for the triple (implementation details tested in KnowledgeGraph)
|
||||
mock_tg_instance.insert.assert_called_once_with(
|
||||
'collection1', 'test_subject', 'test_predicate', 'test_object'
|
||||
'collection1', 'test_subject', 'test_predicate', 'test_object',
|
||||
g=DEFAULT_GRAPH
|
||||
)
|
||||
|
||||
def test_environment_variable_controls_mode(self):
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import pytest
|
|||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from trustgraph.storage.triples.falkordb.write import Processor
|
||||
from trustgraph.schema import Value, Triple
|
||||
from trustgraph.schema import Term, Triple, IRI, LITERAL
|
||||
|
||||
|
||||
class TestFalkorDBStorageProcessor:
|
||||
|
|
@ -22,9 +22,9 @@ class TestFalkorDBStorageProcessor:
|
|||
|
||||
# Create a test triple
|
||||
triple = Triple(
|
||||
s=Value(value='http://example.com/subject', is_uri=True),
|
||||
p=Value(value='http://example.com/predicate', is_uri=True),
|
||||
o=Value(value='literal object', is_uri=False)
|
||||
s=Term(type=IRI, iri='http://example.com/subject'),
|
||||
p=Term(type=IRI, iri='http://example.com/predicate'),
|
||||
o=Term(type=LITERAL, value='literal object')
|
||||
)
|
||||
message.triples = [triple]
|
||||
|
||||
|
|
@ -183,9 +183,9 @@ class TestFalkorDBStorageProcessor:
|
|||
message.metadata.collection = 'test_collection'
|
||||
|
||||
triple = Triple(
|
||||
s=Value(value='http://example.com/subject', is_uri=True),
|
||||
p=Value(value='http://example.com/predicate', is_uri=True),
|
||||
o=Value(value='http://example.com/object', is_uri=True)
|
||||
s=Term(type=IRI, iri='http://example.com/subject'),
|
||||
p=Term(type=IRI, iri='http://example.com/predicate'),
|
||||
o=Term(type=IRI, iri='http://example.com/object')
|
||||
)
|
||||
message.triples = [triple]
|
||||
|
||||
|
|
@ -269,14 +269,14 @@ class TestFalkorDBStorageProcessor:
|
|||
message.metadata.collection = 'test_collection'
|
||||
|
||||
triple1 = Triple(
|
||||
s=Value(value='http://example.com/subject1', is_uri=True),
|
||||
p=Value(value='http://example.com/predicate1', is_uri=True),
|
||||
o=Value(value='literal object1', is_uri=False)
|
||||
s=Term(type=IRI, iri='http://example.com/subject1'),
|
||||
p=Term(type=IRI, iri='http://example.com/predicate1'),
|
||||
o=Term(type=LITERAL, value='literal object1')
|
||||
)
|
||||
triple2 = Triple(
|
||||
s=Value(value='http://example.com/subject2', is_uri=True),
|
||||
p=Value(value='http://example.com/predicate2', is_uri=True),
|
||||
o=Value(value='http://example.com/object2', is_uri=True)
|
||||
s=Term(type=IRI, iri='http://example.com/subject2'),
|
||||
p=Term(type=IRI, iri='http://example.com/predicate2'),
|
||||
o=Term(type=IRI, iri='http://example.com/object2')
|
||||
)
|
||||
message.triples = [triple1, triple2]
|
||||
|
||||
|
|
@ -337,14 +337,14 @@ class TestFalkorDBStorageProcessor:
|
|||
message.metadata.collection = 'test_collection'
|
||||
|
||||
triple1 = Triple(
|
||||
s=Value(value='http://example.com/subject1', is_uri=True),
|
||||
p=Value(value='http://example.com/predicate1', is_uri=True),
|
||||
o=Value(value='literal object', is_uri=False)
|
||||
s=Term(type=IRI, iri='http://example.com/subject1'),
|
||||
p=Term(type=IRI, iri='http://example.com/predicate1'),
|
||||
o=Term(type=LITERAL, value='literal object')
|
||||
)
|
||||
triple2 = Triple(
|
||||
s=Value(value='http://example.com/subject2', is_uri=True),
|
||||
p=Value(value='http://example.com/predicate2', is_uri=True),
|
||||
o=Value(value='http://example.com/object2', is_uri=True)
|
||||
s=Term(type=IRI, iri='http://example.com/subject2'),
|
||||
p=Term(type=IRI, iri='http://example.com/predicate2'),
|
||||
o=Term(type=IRI, iri='http://example.com/object2')
|
||||
)
|
||||
message.triples = [triple1, triple2]
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import pytest
|
|||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from trustgraph.storage.triples.memgraph.write import Processor
|
||||
from trustgraph.schema import Value, Triple
|
||||
from trustgraph.schema import Term, Triple, IRI, LITERAL
|
||||
|
||||
|
||||
class TestMemgraphStorageProcessor:
|
||||
|
|
@ -22,9 +22,9 @@ class TestMemgraphStorageProcessor:
|
|||
|
||||
# Create a test triple
|
||||
triple = Triple(
|
||||
s=Value(value='http://example.com/subject', is_uri=True),
|
||||
p=Value(value='http://example.com/predicate', is_uri=True),
|
||||
o=Value(value='literal object', is_uri=False)
|
||||
s=Term(type=IRI, iri='http://example.com/subject'),
|
||||
p=Term(type=IRI, iri='http://example.com/predicate'),
|
||||
o=Term(type=LITERAL, value='literal object')
|
||||
)
|
||||
message.triples = [triple]
|
||||
|
||||
|
|
@ -231,9 +231,9 @@ class TestMemgraphStorageProcessor:
|
|||
mock_tx = MagicMock()
|
||||
|
||||
triple = Triple(
|
||||
s=Value(value='http://example.com/subject', is_uri=True),
|
||||
p=Value(value='http://example.com/predicate', is_uri=True),
|
||||
o=Value(value='http://example.com/object', is_uri=True)
|
||||
s=Term(type=IRI, iri='http://example.com/subject'),
|
||||
p=Term(type=IRI, iri='http://example.com/predicate'),
|
||||
o=Term(type=IRI, iri='http://example.com/object')
|
||||
)
|
||||
|
||||
processor.create_triple(mock_tx, triple, "test_user", "test_collection")
|
||||
|
|
@ -265,9 +265,9 @@ class TestMemgraphStorageProcessor:
|
|||
mock_tx = MagicMock()
|
||||
|
||||
triple = Triple(
|
||||
s=Value(value='http://example.com/subject', is_uri=True),
|
||||
p=Value(value='http://example.com/predicate', is_uri=True),
|
||||
o=Value(value='literal object', is_uri=False)
|
||||
s=Term(type=IRI, iri='http://example.com/subject'),
|
||||
p=Term(type=IRI, iri='http://example.com/predicate'),
|
||||
o=Term(type=LITERAL, value='literal object')
|
||||
)
|
||||
|
||||
processor.create_triple(mock_tx, triple, "test_user", "test_collection")
|
||||
|
|
@ -347,14 +347,14 @@ class TestMemgraphStorageProcessor:
|
|||
message.metadata.collection = 'test_collection'
|
||||
|
||||
triple1 = Triple(
|
||||
s=Value(value='http://example.com/subject1', is_uri=True),
|
||||
p=Value(value='http://example.com/predicate1', is_uri=True),
|
||||
o=Value(value='literal object1', is_uri=False)
|
||||
s=Term(type=IRI, iri='http://example.com/subject1'),
|
||||
p=Term(type=IRI, iri='http://example.com/predicate1'),
|
||||
o=Term(type=LITERAL, value='literal object1')
|
||||
)
|
||||
triple2 = Triple(
|
||||
s=Value(value='http://example.com/subject2', is_uri=True),
|
||||
p=Value(value='http://example.com/predicate2', is_uri=True),
|
||||
o=Value(value='http://example.com/object2', is_uri=True)
|
||||
s=Term(type=IRI, iri='http://example.com/subject2'),
|
||||
p=Term(type=IRI, iri='http://example.com/predicate2'),
|
||||
o=Term(type=IRI, iri='http://example.com/object2')
|
||||
)
|
||||
message.triples = [triple1, triple2]
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import pytest
|
|||
from unittest.mock import MagicMock, patch, AsyncMock
|
||||
|
||||
from trustgraph.storage.triples.neo4j.write import Processor
|
||||
from trustgraph.schema import IRI, LITERAL
|
||||
|
||||
|
||||
class TestNeo4jStorageProcessor:
|
||||
|
|
@ -257,10 +258,12 @@ class TestNeo4jStorageProcessor:
|
|||
|
||||
# Create mock triple with URI object
|
||||
triple = MagicMock()
|
||||
triple.s.value = "http://example.com/subject"
|
||||
triple.p.value = "http://example.com/predicate"
|
||||
triple.o.value = "http://example.com/object"
|
||||
triple.o.is_uri = True
|
||||
triple.s.type = IRI
|
||||
triple.s.iri = "http://example.com/subject"
|
||||
triple.p.type = IRI
|
||||
triple.p.iri = "http://example.com/predicate"
|
||||
triple.o.type = IRI
|
||||
triple.o.iri = "http://example.com/object"
|
||||
|
||||
# Create mock message with metadata
|
||||
mock_message = MagicMock()
|
||||
|
|
@ -327,10 +330,12 @@ class TestNeo4jStorageProcessor:
|
|||
|
||||
# Create mock triple with literal object
|
||||
triple = MagicMock()
|
||||
triple.s.value = "http://example.com/subject"
|
||||
triple.p.value = "http://example.com/predicate"
|
||||
triple.s.type = IRI
|
||||
triple.s.iri = "http://example.com/subject"
|
||||
triple.p.type = IRI
|
||||
triple.p.iri = "http://example.com/predicate"
|
||||
triple.o.type = LITERAL
|
||||
triple.o.value = "literal value"
|
||||
triple.o.is_uri = False
|
||||
|
||||
# Create mock message with metadata
|
||||
mock_message = MagicMock()
|
||||
|
|
@ -398,16 +403,20 @@ class TestNeo4jStorageProcessor:
|
|||
|
||||
# Create mock triples
|
||||
triple1 = MagicMock()
|
||||
triple1.s.value = "http://example.com/subject1"
|
||||
triple1.p.value = "http://example.com/predicate1"
|
||||
triple1.o.value = "http://example.com/object1"
|
||||
triple1.o.is_uri = True
|
||||
|
||||
triple1.s.type = IRI
|
||||
triple1.s.iri = "http://example.com/subject1"
|
||||
triple1.p.type = IRI
|
||||
triple1.p.iri = "http://example.com/predicate1"
|
||||
triple1.o.type = IRI
|
||||
triple1.o.iri = "http://example.com/object1"
|
||||
|
||||
triple2 = MagicMock()
|
||||
triple2.s.value = "http://example.com/subject2"
|
||||
triple2.p.value = "http://example.com/predicate2"
|
||||
triple2.s.type = IRI
|
||||
triple2.s.iri = "http://example.com/subject2"
|
||||
triple2.p.type = IRI
|
||||
triple2.p.iri = "http://example.com/predicate2"
|
||||
triple2.o.type = LITERAL
|
||||
triple2.o.value = "literal value"
|
||||
triple2.o.is_uri = False
|
||||
|
||||
# Create mock message with metadata
|
||||
mock_message = MagicMock()
|
||||
|
|
@ -550,10 +559,12 @@ class TestNeo4jStorageProcessor:
|
|||
|
||||
# Create triple with special characters
|
||||
triple = MagicMock()
|
||||
triple.s.value = "http://example.com/subject with spaces"
|
||||
triple.p.value = "http://example.com/predicate:with/symbols"
|
||||
triple.s.type = IRI
|
||||
triple.s.iri = "http://example.com/subject with spaces"
|
||||
triple.p.type = IRI
|
||||
triple.p.iri = "http://example.com/predicate:with/symbols"
|
||||
triple.o.type = LITERAL
|
||||
triple.o.value = 'literal with "quotes" and unicode: ñáéíóú'
|
||||
triple.o.is_uri = False
|
||||
|
||||
mock_message = MagicMock()
|
||||
mock_message.triples = [triple]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue