From 6b7b710fe5daab75e855acc357fcf5676a0cc8be Mon Sep 17 00:00:00 2001 From: Cyber MacGeddon Date: Tue, 27 Jan 2026 00:26:43 +0000 Subject: [PATCH] Tests --- .../test_agent_kg_extraction_integration.py | 22 +-- .../integration/test_cassandra_integration.py | 108 +++++------ .../test_kg_extract_store_integration.py | 98 +++++----- .../test_ontology/test_entity_contexts.py | 172 +++++++++--------- .../test_ontology/test_ontology_triples.py | 62 +++---- tests/unit/test_knowledge_graph/conftest.py | 48 +++-- .../test_agent_extraction.py | 88 ++++----- .../test_agent_extraction_edge_cases.py | 14 +- 8 files changed, 310 insertions(+), 302 deletions(-) diff --git a/tests/integration/test_agent_kg_extraction_integration.py b/tests/integration/test_agent_kg_extraction_integration.py index 01516d8b..849547c8 100644 --- a/tests/integration/test_agent_kg_extraction_integration.py +++ b/tests/integration/test_agent_kg_extraction_integration.py @@ -12,7 +12,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, AgentRequest, AgentResponse from trustgraph.rdf import TRUSTGRAPH_ENTITIES, DEFINITION, RDF_LABEL, SUBJECT_OF from trustgraph.template.prompt_manager import PromptManager @@ -78,9 +78,9 @@ class TestAgentKgExtractionIntegration: 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,15 +178,15 @@ class TestAgentKgExtractionIntegration: assert len(sent_triples.triples) > 0 # Check that we have definition triples - definition_triples = [t for t in sent_triples.triples if t.p.value == DEFINITION] + definition_triples = [t for t in sent_triples.triples if t.p.iri == DEFINITION] assert len(definition_triples) >= 2 # Should have definitions for ML and Neural Networks - + # Check that we have label triples - label_triples = [t for t in sent_triples.triples if t.p.value == RDF_LABEL] + label_triples = [t for t in sent_triples.triples if t.p.iri == RDF_LABEL] assert len(label_triples) >= 2 # Should have labels for entities - + # Check subject-of relationships - subject_of_triples = [t for t in sent_triples.triples if t.p.value == SUBJECT_OF] + subject_of_triples = [t for t in sent_triples.triples if t.p.iri == SUBJECT_OF] assert len(subject_of_triples) >= 2 # Entities should be linked to document # Verify entity contexts were emitted @@ -198,7 +198,7 @@ class TestAgentKgExtractionIntegration: assert len(sent_contexts.entities) >= 2 # Should have contexts for both entities # Verify entity URIs are properly formed - entity_uris = [ec.entity.value for ec in sent_contexts.entities] + entity_uris = [ec.entity.iri for ec in sent_contexts.entities] assert f"{TRUSTGRAPH_ENTITIES}Machine%20Learning" in entity_uris assert f"{TRUSTGRAPH_ENTITIES}Neural%20Networks" in entity_uris @@ -401,7 +401,7 @@ class TestAgentKgExtractionIntegration: sent_triples = triples_publisher.send.call_args[0][0] # Check that unicode entity was properly processed - entity_labels = [t for t in sent_triples.triples if t.p.value == RDF_LABEL and t.o.value == "機械学習"] + entity_labels = [t for t in sent_triples.triples if t.p.iri == RDF_LABEL and t.o.value == "機械学習"] assert len(entity_labels) > 0 @pytest.mark.asyncio diff --git a/tests/integration/test_cassandra_integration.py b/tests/integration/test_cassandra_integration.py index 560f3132..2f5a4195 100644 --- a/tests/integration/test_cassandra_integration.py +++ b/tests/integration/test_cassandra_integration.py @@ -16,7 +16,7 @@ from .cassandra_test_helper import cassandra_container from trustgraph.direct.cassandra_kg import KnowledgeGraph from trustgraph.storage.triples.cassandra.write import Processor as StorageProcessor from trustgraph.query.triples.cassandra.service import Processor as QueryProcessor -from trustgraph.schema import Triple, Value, Metadata, Triples, TriplesQueryRequest +from trustgraph.schema import Triple, Term, Metadata, Triples, TriplesQueryRequest, IRI, LITERAL @pytest.mark.integration @@ -118,19 +118,19 @@ class TestCassandraIntegration: metadata=Metadata(user="testuser", collection="testcol"), triples=[ Triple( - s=Value(value="http://example.org/person1", is_uri=True), - p=Value(value="http://example.org/name", is_uri=True), - o=Value(value="Alice Smith", is_uri=False) + s=Term(type=IRI, iri="http://example.org/person1"), + p=Term(type=IRI, iri="http://example.org/name"), + o=Term(type=LITERAL, value="Alice Smith") ), Triple( - s=Value(value="http://example.org/person1", is_uri=True), - p=Value(value="http://example.org/age", is_uri=True), - o=Value(value="25", is_uri=False) + s=Term(type=IRI, iri="http://example.org/person1"), + p=Term(type=IRI, iri="http://example.org/age"), + o=Term(type=LITERAL, value="25") ), Triple( - s=Value(value="http://example.org/person1", is_uri=True), - p=Value(value="http://example.org/department", is_uri=True), - o=Value(value="Engineering", is_uri=False) + s=Term(type=IRI, iri="http://example.org/person1"), + p=Term(type=IRI, iri="http://example.org/department"), + o=Term(type=LITERAL, value="Engineering") ) ] ) @@ -181,19 +181,19 @@ class TestCassandraIntegration: metadata=Metadata(user="testuser", collection="testcol"), triples=[ Triple( - s=Value(value="http://example.org/alice", is_uri=True), - p=Value(value="http://example.org/knows", is_uri=True), - o=Value(value="http://example.org/bob", is_uri=True) + s=Term(type=IRI, iri="http://example.org/alice"), + p=Term(type=IRI, iri="http://example.org/knows"), + o=Term(type=IRI, iri="http://example.org/bob") ), Triple( - s=Value(value="http://example.org/alice", is_uri=True), - p=Value(value="http://example.org/age", is_uri=True), - o=Value(value="30", is_uri=False) + s=Term(type=IRI, iri="http://example.org/alice"), + p=Term(type=IRI, iri="http://example.org/age"), + o=Term(type=LITERAL, value="30") ), Triple( - s=Value(value="http://example.org/bob", is_uri=True), - p=Value(value="http://example.org/knows", is_uri=True), - o=Value(value="http://example.org/charlie", is_uri=True) + s=Term(type=IRI, iri="http://example.org/bob"), + p=Term(type=IRI, iri="http://example.org/knows"), + o=Term(type=IRI, iri="http://example.org/charlie") ) ] ) @@ -208,7 +208,7 @@ class TestCassandraIntegration: # Test S query (find all relationships for Alice) s_query = TriplesQueryRequest( - s=Value(value="http://example.org/alice", is_uri=True), + s=Term(type=IRI, iri="http://example.org/alice"), p=None, # None for wildcard o=None, # None for wildcard limit=10, @@ -218,18 +218,18 @@ class TestCassandraIntegration: s_results = await query_processor.query_triples(s_query) print(f"Query processor results: {len(s_results)}") for result in s_results: - print(f" S={result.s.value}, P={result.p.value}, O={result.o.value}") + print(f" S={result.s.iri}, P={result.p.iri}, O={result.o.iri if result.o.type == IRI else result.o.value}") assert len(s_results) == 2 - - s_predicates = [t.p.value for t in s_results] + + s_predicates = [t.p.iri for t in s_results] assert "http://example.org/knows" in s_predicates assert "http://example.org/age" in s_predicates print("✓ Subject queries via processor working") - + # Test P query (find all "knows" relationships) p_query = TriplesQueryRequest( s=None, # None for wildcard - p=Value(value="http://example.org/knows", is_uri=True), + p=Term(type=IRI, iri="http://example.org/knows"), o=None, # None for wildcard limit=10, user="testuser", @@ -238,8 +238,8 @@ class TestCassandraIntegration: p_results = await query_processor.query_triples(p_query) print(p_results) assert len(p_results) == 2 # Alice knows Bob, Bob knows Charlie - - p_subjects = [t.s.value for t in p_results] + + p_subjects = [t.s.iri for t in p_results] assert "http://example.org/alice" in p_subjects assert "http://example.org/bob" in p_subjects print("✓ Predicate queries via processor working") @@ -262,19 +262,19 @@ class TestCassandraIntegration: metadata=Metadata(user="concurrent_test", collection="people"), triples=[ Triple( - s=Value(value=f"http://example.org/{person_id}", is_uri=True), - p=Value(value="http://example.org/name", is_uri=True), - o=Value(value=name, is_uri=False) + s=Term(type=IRI, iri=f"http://example.org/{person_id}"), + p=Term(type=IRI, iri="http://example.org/name"), + o=Term(type=LITERAL, value=name) ), Triple( - s=Value(value=f"http://example.org/{person_id}", is_uri=True), - p=Value(value="http://example.org/age", is_uri=True), - o=Value(value=str(age), is_uri=False) + s=Term(type=IRI, iri=f"http://example.org/{person_id}"), + p=Term(type=IRI, iri="http://example.org/age"), + o=Term(type=LITERAL, value=str(age)) ), Triple( - s=Value(value=f"http://example.org/{person_id}", is_uri=True), - p=Value(value="http://example.org/department", is_uri=True), - o=Value(value=department, is_uri=False) + s=Term(type=IRI, iri=f"http://example.org/{person_id}"), + p=Term(type=IRI, iri="http://example.org/department"), + o=Term(type=LITERAL, value=department) ) ] ) @@ -333,36 +333,36 @@ class TestCassandraIntegration: triples=[ # People and their types Triple( - s=Value(value="http://company.org/alice", is_uri=True), - p=Value(value="http://www.w3.org/1999/02/22-rdf-syntax-ns#type", is_uri=True), - o=Value(value="http://company.org/Employee", is_uri=True) + s=Term(type=IRI, iri="http://company.org/alice"), + p=Term(type=IRI, iri="http://www.w3.org/1999/02/22-rdf-syntax-ns#type"), + o=Term(type=IRI, iri="http://company.org/Employee") ), Triple( - s=Value(value="http://company.org/bob", is_uri=True), - p=Value(value="http://www.w3.org/1999/02/22-rdf-syntax-ns#type", is_uri=True), - o=Value(value="http://company.org/Employee", is_uri=True) + s=Term(type=IRI, iri="http://company.org/bob"), + p=Term(type=IRI, iri="http://www.w3.org/1999/02/22-rdf-syntax-ns#type"), + o=Term(type=IRI, iri="http://company.org/Employee") ), # Relationships Triple( - s=Value(value="http://company.org/alice", is_uri=True), - p=Value(value="http://company.org/reportsTo", is_uri=True), - o=Value(value="http://company.org/bob", is_uri=True) + s=Term(type=IRI, iri="http://company.org/alice"), + p=Term(type=IRI, iri="http://company.org/reportsTo"), + o=Term(type=IRI, iri="http://company.org/bob") ), Triple( - s=Value(value="http://company.org/alice", is_uri=True), - p=Value(value="http://company.org/worksIn", is_uri=True), - o=Value(value="http://company.org/engineering", is_uri=True) + s=Term(type=IRI, iri="http://company.org/alice"), + p=Term(type=IRI, iri="http://company.org/worksIn"), + o=Term(type=IRI, iri="http://company.org/engineering") ), # Personal info Triple( - s=Value(value="http://company.org/alice", is_uri=True), - p=Value(value="http://company.org/fullName", is_uri=True), - o=Value(value="Alice Johnson", is_uri=False) + s=Term(type=IRI, iri="http://company.org/alice"), + p=Term(type=IRI, iri="http://company.org/fullName"), + o=Term(type=LITERAL, value="Alice Johnson") ), Triple( - s=Value(value="http://company.org/alice", is_uri=True), - p=Value(value="http://company.org/email", is_uri=True), - o=Value(value="alice@company.org", is_uri=False) + s=Term(type=IRI, iri="http://company.org/alice"), + p=Term(type=IRI, iri="http://company.org/email"), + o=Term(type=LITERAL, value="alice@company.org") ), ] ) diff --git a/tests/integration/test_kg_extract_store_integration.py b/tests/integration/test_kg_extract_store_integration.py index dd13789f..3982cc13 100644 --- a/tests/integration/test_kg_extract_store_integration.py +++ b/tests/integration/test_kg_extract_store_integration.py @@ -15,7 +15,7 @@ from unittest.mock import AsyncMock, MagicMock, patch from trustgraph.extract.kg.definitions.extract import Processor as DefinitionsProcessor from trustgraph.extract.kg.relationships.extract import Processor as RelationshipsProcessor from trustgraph.storage.knowledge.store import Processor as KnowledgeStoreProcessor -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, GraphEmbeddings from trustgraph.rdf import TRUSTGRAPH_ENTITIES, DEFINITION, RDF_LABEL, SUBJECT_OF @@ -253,24 +253,24 @@ class TestKnowledgeGraphPipelineIntegration: if s and o: s_uri = definitions_processor.to_uri(s) - s_value = Value(value=str(s_uri), is_uri=True) - o_value = Value(value=str(o), is_uri=False) - + s_term = Term(type=IRI, iri=str(s_uri)) + o_term = Term(type=LITERAL, value=str(o)) + # Generate triples as the processor would triples.append(Triple( - s=s_value, - p=Value(value=RDF_LABEL, is_uri=True), - o=Value(value=s, is_uri=False) + s=s_term, + p=Term(type=IRI, iri=RDF_LABEL), + o=Term(type=LITERAL, value=s) )) - + triples.append(Triple( - s=s_value, - p=Value(value=DEFINITION, is_uri=True), - o=o_value + s=s_term, + p=Term(type=IRI, iri=DEFINITION), + o=o_term )) - + entities.append(EntityContext( - entity=s_value, + entity=s_term, context=defn["definition"] )) @@ -279,16 +279,16 @@ class TestKnowledgeGraphPipelineIntegration: assert len(entities) == 3 # 1 entity context per entity # Verify triple structure - label_triples = [t for t in triples if t.p.value == RDF_LABEL] - definition_triples = [t for t in triples if t.p.value == DEFINITION] - + label_triples = [t for t in triples if t.p.iri == RDF_LABEL] + definition_triples = [t for t in triples if t.p.iri == DEFINITION] + assert len(label_triples) == 3 assert len(definition_triples) == 3 - + # Verify entity contexts for entity in entities: - assert entity.entity.is_uri is True - assert entity.entity.value.startswith(TRUSTGRAPH_ENTITIES) + assert entity.entity.type == IRI + assert entity.entity.iri.startswith(TRUSTGRAPH_ENTITIES) assert len(entity.context) > 0 @pytest.mark.asyncio @@ -309,52 +309,52 @@ class TestKnowledgeGraphPipelineIntegration: s = rel["subject"] p = rel["predicate"] o = rel["object"] - + if s and p and o: s_uri = relationships_processor.to_uri(s) - s_value = Value(value=str(s_uri), is_uri=True) - + s_term = Term(type=IRI, iri=str(s_uri)) + p_uri = relationships_processor.to_uri(p) - p_value = Value(value=str(p_uri), is_uri=True) - + p_term = Term(type=IRI, iri=str(p_uri)) + if rel["object-entity"]: o_uri = relationships_processor.to_uri(o) - o_value = Value(value=str(o_uri), is_uri=True) + o_term = Term(type=IRI, iri=str(o_uri)) else: - o_value = Value(value=str(o), is_uri=False) - + o_term = Term(type=LITERAL, value=str(o)) + # Main relationship triple - triples.append(Triple(s=s_value, p=p_value, o=o_value)) - + triples.append(Triple(s=s_term, p=p_term, o=o_term)) + # Label triples triples.append(Triple( - s=s_value, - p=Value(value=RDF_LABEL, is_uri=True), - o=Value(value=str(s), is_uri=False) + s=s_term, + p=Term(type=IRI, iri=RDF_LABEL), + o=Term(type=LITERAL, value=str(s)) )) - + triples.append(Triple( - s=p_value, - p=Value(value=RDF_LABEL, is_uri=True), - o=Value(value=str(p), is_uri=False) + s=p_term, + p=Term(type=IRI, iri=RDF_LABEL), + o=Term(type=LITERAL, value=str(p)) )) - + if rel["object-entity"]: triples.append(Triple( - s=o_value, - p=Value(value=RDF_LABEL, is_uri=True), - o=Value(value=str(o), is_uri=False) + s=o_term, + p=Term(type=IRI, iri=RDF_LABEL), + o=Term(type=LITERAL, value=str(o)) )) # Assert assert len(triples) > 0 # Verify relationship triples exist - relationship_triples = [t for t in triples if t.p.value.endswith("is_subset_of") or t.p.value.endswith("is_used_in")] + relationship_triples = [t for t in triples if t.p.iri.endswith("is_subset_of") or t.p.iri.endswith("is_used_in")] assert len(relationship_triples) >= 2 - + # Verify label triples - label_triples = [t for t in triples if t.p.value == RDF_LABEL] + label_triples = [t for t in triples if t.p.iri == RDF_LABEL] assert len(label_triples) > 0 @pytest.mark.asyncio @@ -374,9 +374,9 @@ class TestKnowledgeGraphPipelineIntegration: ), triples=[ Triple( - s=Value(value="http://trustgraph.ai/e/machine-learning", is_uri=True), - p=Value(value=DEFINITION, is_uri=True), - o=Value(value="A subset of AI", is_uri=False) + s=Term(type=IRI, iri="http://trustgraph.ai/e/machine-learning"), + p=Term(type=IRI, iri=DEFINITION), + o=Term(type=LITERAL, value="A subset of AI") ) ] ) @@ -602,9 +602,9 @@ class TestKnowledgeGraphPipelineIntegration: collection="test_collection", metadata=[ Triple( - s=Value(value="doc:test", is_uri=True), - p=Value(value="dc:title", is_uri=True), - o=Value(value="Test Document", is_uri=False) + s=Term(type=IRI, iri="doc:test"), + p=Term(type=IRI, iri="dc:title"), + o=Term(type=LITERAL, value="Test Document") ) ] ) diff --git a/tests/unit/test_extract/test_ontology/test_entity_contexts.py b/tests/unit/test_extract/test_ontology/test_entity_contexts.py index c867b05a..fde24e58 100644 --- a/tests/unit/test_extract/test_ontology/test_entity_contexts.py +++ b/tests/unit/test_extract/test_ontology/test_entity_contexts.py @@ -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 \"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 \"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") ) ] diff --git a/tests/unit/test_extract/test_ontology/test_ontology_triples.py b/tests/unit/test_extract/test_ontology/test_ontology_triples.py index 70ade79d..f8d51a3a 100644 --- a/tests/unit/test_extract/test_ontology/test_ontology_triples.py +++ b/tests/unit/test_extract/test_ontology/test_ontology_triples.py @@ -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,8 +221,8 @@ 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, \ @@ -236,8 +236,8 @@ 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" + 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.value == "http://www.w3.org/2002/07/owl#Class" ) @@ -250,8 +250,8 @@ 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" + 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.value or "DatatypeProperty" in t.o.value) ) @@ -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" diff --git a/tests/unit/test_knowledge_graph/conftest.py b/tests/unit/test_knowledge_graph/conftest.py index d4a83054..e7f83b58 100644 --- a/tests/unit/test_knowledge_graph/conftest.py +++ b/tests/unit/test_knowledge_graph/conftest.py @@ -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 ) diff --git a/tests/unit/test_knowledge_graph/test_agent_extraction.py b/tests/unit/test_knowledge_graph/test_agent_extraction.py index 626eba42..a3a0f9a7 100644 --- a/tests/unit/test_knowledge_graph/test_agent_extraction.py +++ b/tests/unit/test_knowledge_graph/test_agent_extraction.py @@ -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""" diff --git a/tests/unit/test_knowledge_graph/test_agent_extraction_edge_cases.py b/tests/unit/test_knowledge_graph/test_agent_extraction_edge_cases.py index a5190d4d..f66e5da6 100644 --- a/tests/unit/test_knowledge_graph/test_agent_extraction_edge_cases.py +++ b/tests/unit/test_knowledge_graph/test_agent_extraction_edge_cases.py @@ -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):