Files
AI/참고/ontocast-main/test/test_graph_update.py

528 lines
16 KiB
Python
Raw Permalink Normal View History

2026-05-12 19:40:31 +09:00
"""Test for GraphUpdate SPARQL query generation and execution.
This test verifies that GraphUpdate.generate_sparql_queries() generates valid SPARQL
queries that can be executed on RDFGraph instances using rdflib's update() method.
"""
from rdflib import Literal, URIRef
from ontocast.onto.rdfgraph import RDFGraph
from ontocast.onto.sparql_models import (
GenericSparqlQuery,
GraphUpdate,
TripleOp,
)
def test_rdfgraph_recovers_dangling_semicolon_at_eof() -> None:
"""RDFGraph should recover from common LLM-truncated Turtle at EOF."""
ttl = """
@prefix ex: <http://example.org/> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
ex:Case85_968 a ex:Appeal ;
ex:appealsTo ex:Cassation ;
"""
graph = RDFGraph._from_turtle_str(ttl)
assert len(graph) == 2
assert (
URIRef("http://example.org/Case85_968"),
URIRef("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"),
URIRef("http://example.org/Appeal"),
) in graph
assert (
URIRef("http://example.org/Case85_968"),
URIRef("http://example.org/appealsTo"),
URIRef("http://example.org/Cassation"),
) in graph
def test_graph_update_with_language_tags():
"""Test GraphUpdate with language-tagged literals."""
# Create initial RDFGraph
graph = RDFGraph._from_turtle_str(
"""
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix ex: <http://example.org/> .
ex:Test a rdfs:Class .
"""
)
initial_triple_count = len(graph)
# Create Turtle with language-tagged literals
triples = """
@prefix ex: <http://example.org/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
ex:Test rdfs:label "Test Label"@en ;
rdfs:comment "Un commentaire"@fr .
"""
graph_update = GraphUpdate(
triple_operations=[
TripleOp(
type="insert",
graph=triples, # type: ignore[arg-type]
prefixes={"ex": "http://example.org/"},
)
]
)
# Generate SPARQL queries
queries = graph_update.generate_sparql_queries()
# Should generate one query
assert len(queries) == 1
# Execute the query on the graph
graph.update(queries[0])
# Verify new triples were added
assert len(graph) == initial_triple_count + 2
def test_graph_update_insert_operation():
"""Test GraphUpdate with TripleOp insert operations using Turtle format."""
# Create initial RDFGraph
graph = RDFGraph._from_turtle_str(
"""
@prefix ex: <http://example.org/> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
ex:Person a rdfs:Class ;
rdfs:label "Person" .
"""
)
initial_triple_count = len(graph)
# Create triples in Turtle format
triples = """
@prefix ex: <http://example.org/> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
ex:John a ex:Person ;
rdfs:label "John Doe" .
"""
graph_update = GraphUpdate(
triple_operations=[
TripleOp(
type="insert",
graph=triples, # type: ignore[arg-type]
prefixes={"ex": "http://example.org/"},
)
]
)
# Generate SPARQL queries
queries = graph_update.generate_sparql_queries()
# Should generate one query
assert len(queries) == 1
# Execute the query on the graph
graph.update(queries[0])
# Verify new triples were added
assert len(graph) == initial_triple_count + 2
assert (
URIRef("http://example.org/John"),
URIRef("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"),
URIRef("http://example.org/Person"),
) in graph
assert (
URIRef("http://example.org/John"),
URIRef("http://www.w3.org/2000/01/rdf-schema#label"),
Literal("John Doe"),
) in graph
def test_graph_update_extract_insert_graph() -> None:
"""Test GraphUpdate.extract_insert_graph returns only insert triples."""
insert_ttl = """
@prefix ex: <http://example.org/> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
ex:Person a rdfs:Class .
ex:Person rdfs:label "Person" .
"""
delete_ttl = """
@prefix ex: <http://example.org/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
ex:Obsolete a rdfs:Class .
"""
gu = GraphUpdate(
triple_operations=[
TripleOp(type="insert", graph=insert_ttl), # type: ignore[arg-type]
TripleOp(type="delete", graph=delete_ttl), # type: ignore[arg-type]
]
)
insert_graph = gu.extract_insert_graph()
assert len(insert_graph) == 2
person_uri = URIRef("http://example.org/Person")
rdf_type = URIRef("http://www.w3.org/1999/02/22-rdf-syntax-ns#type")
rdfs_class = URIRef("http://www.w3.org/2000/01/rdf-schema#Class")
rdfs_label = URIRef("http://www.w3.org/2000/01/rdf-schema#label")
assert (person_uri, rdf_type, rdfs_class) in insert_graph
assert (person_uri, rdfs_label, Literal("Person")) in insert_graph
obsolete_uri = URIRef("http://example.org/Obsolete")
assert (obsolete_uri, rdf_type, rdfs_class) not in insert_graph
def test_graph_update_delete_operation():
"""Test GraphUpdate with TripleOp delete operations."""
# Create RDFGraph with existing triples
graph = RDFGraph._from_turtle_str(
"""
@prefix ex: <http://example.org/> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
ex:Person a rdfs:Class ;
rdfs:label "Person" .
ex:John a ex:Person ;
rdfs:label "John Doe" .
ex:Jane a ex:Person ;
rdfs:label "Jane Smith" .
"""
)
initial_triple_count = len(graph)
# Create GraphUpdate with TripleOp using Turtle format
triples = """
@prefix ex: <http://example.org/> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
ex:John a ex:Person ;
rdfs:label "John Doe" .
"""
graph_update = GraphUpdate(
triple_operations=[
TripleOp(
type="delete",
graph=triples, # type: ignore[arg-type]
prefixes={"ex": "http://example.org/"},
)
]
)
# Generate SPARQL queries
queries = graph_update.generate_sparql_queries()
# Should generate one query
assert len(queries) == 1
# Execute the query on the graph
graph.update(queries[0])
# Verify triples were removed
assert len(graph) == initial_triple_count - 2
assert (
URIRef("http://example.org/John"),
URIRef("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"),
URIRef("http://example.org/Person"),
) not in graph
assert (
URIRef("http://example.org/John"),
URIRef("http://www.w3.org/2000/01/rdf-schema#label"),
Literal("John Doe"),
) not in graph
# Jane should still be there
assert (
URIRef("http://example.org/Jane"),
URIRef("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"),
URIRef("http://example.org/Person"),
) in graph
def test_graph_update_with_prefixes():
"""Test GraphUpdate with TripleOp operations that declare custom prefixes."""
# Create initial RDFGraph
graph = RDFGraph._from_turtle_str(
"""
@prefix ex: <http://example.org/> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
ex:Person a rdf:Class .
"""
)
initial_triple_count = len(graph)
# Create GraphUpdate with custom prefixes using Turtle format
triples = """
@prefix ex: <http://example.org/> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix schema: <https://schema.org/> .
ex:John a ex:Person ;
schema:name "John Doe" .
"""
graph_update = GraphUpdate(
triple_operations=[
TripleOp(
type="insert",
graph=triples, # type: ignore[arg-type]
prefixes={
"ex": "http://example.org/",
"schema": "https://schema.org/",
},
),
]
)
# Generate SPARQL queries
queries = graph_update.generate_sparql_queries()
# Should generate one query
assert len(queries) == 1
# Verify the query includes PREFIX declarations
assert "PREFIX schema: <https://schema.org/>" in queries[0]
# Execute the query on the graph
graph.update(queries[0])
# Verify new triples were added
assert len(graph) == initial_triple_count + 2
assert (
URIRef("http://example.org/John"),
URIRef("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"),
URIRef("http://example.org/Person"),
) in graph
assert (
URIRef("http://example.org/John"),
URIRef("https://schema.org/name"),
Literal("John Doe"),
) in graph
def test_graph_update_mixed_operations_ordered():
"""Test GraphUpdate with mixed operations in specific order."""
# Create initial RDFGraph
graph = RDFGraph._from_turtle_str(
"""
@prefix ex: <http://example.org/> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
ex:Person a rdfs:Class ;
rdfs:label "Person" .
ex:John a ex:Person ;
rdfs:label "John Doe" .
"""
)
initial_triple_count = len(graph)
# Create GraphUpdate with mixed operations using Turtle format
insert_jane = """
@prefix ex: <http://example.org/> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix schema: <https://schema.org/> .
ex:Jane a ex:Person ;
schema:name "Jane Smith" .
"""
delete_john_label = """
@prefix ex: <http://example.org/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
ex:John rdfs:label "John Doe" .
"""
insert_john_label = """
@prefix ex: <http://example.org/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
ex:John rdfs:label "John Updated" .
"""
graph_update = GraphUpdate(
triple_operations=[
# First: Insert new person with custom schema prefix
TripleOp(
type="insert",
graph=insert_jane, # type: ignore[arg-type]
prefixes={
"ex": "http://example.org/",
"schema": "https://schema.org/",
},
),
# Second: Delete John's label
TripleOp(
type="delete",
graph=delete_john_label, # type: ignore[arg-type]
prefixes={"ex": "http://example.org/"},
),
# Third: Insert new label for John
TripleOp(
type="insert",
graph=insert_john_label, # type: ignore[arg-type]
prefixes={"ex": "http://example.org/"},
),
]
)
# Generate SPARQL queries
queries = graph_update.generate_sparql_queries()
# Should generate 3 queries (one for each TripleOp)
assert len(queries) == 3
# Execute queries in order
for query in queries:
graph.update(query)
# Verify final state
# Should have: 4 initial + 2 added (Jane) - 1 deleted (John's old label) + 1 added (John's new label) = 6 triples
assert (
len(graph) == initial_triple_count + 2
) # +2 net change: +2 for Jane, -1 for John's old label, +1 for John's new label
# Verify John's label was updated
assert (
URIRef("http://example.org/John"),
URIRef("http://www.w3.org/2000/01/rdf-schema#label"),
Literal("John Updated"),
) in graph
assert (
URIRef("http://example.org/John"),
URIRef("http://www.w3.org/2000/01/rdf-schema#label"),
Literal("John Doe"),
) not in graph
# Verify Jane was added
assert (
URIRef("http://example.org/Jane"),
URIRef("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"),
URIRef("http://example.org/Person"),
) in graph
assert (
URIRef("http://example.org/Jane"),
URIRef("https://schema.org/name"),
Literal("Jane Smith"),
) in graph
def test_graph_update_generic_sparql_query():
"""Test GraphUpdate with GenericSparqlQuery operation."""
# Create initial RDFGraph
graph = RDFGraph._from_turtle_str(
"""
@prefix ex: <http://example.org/> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
ex:Person a rdfs:Class ;
rdfs:label "Person" .
ex:John a ex:Person ;
rdfs:label "John Doe" .
"""
)
initial_triple_count = len(graph)
# Create GraphUpdate with GenericSparqlQuery
# Note: GenericSparqlQuery handles its own prefix declarations
graph_update = GraphUpdate(
sparql_operations=[
GenericSparqlQuery(
query="PREFIX ex: <http://example.org/>\nPREFIX schema: <https://schema.org/>\nPREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\nINSERT { ex:John schema:age 30 } WHERE { ex:John rdf:type ex:Person }"
),
]
)
# Generate SPARQL queries
queries = graph_update.generate_sparql_queries()
# Should generate one query
assert len(queries) == 1
# Verify the query includes the custom SPARQL with prefixes
assert "INSERT { ex:John schema:age 30 }" in queries[0]
assert "WHERE { ex:John rdf:type ex:Person }" in queries[0]
# Execute the query on the graph
graph.update(queries[0])
# Verify the custom query was executed
assert len(graph) == initial_triple_count + 1
assert (
URIRef("http://example.org/John"),
URIRef("https://schema.org/age"),
Literal(30),
) in graph
def test_graph_update_empty_operations():
"""Test GraphUpdate with empty operations list."""
graph = RDFGraph._from_turtle_str(
"""
@prefix ex: <http://example.org/> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
ex:Person a rdf:Class .
"""
)
initial_triple_count = len(graph)
# Create GraphUpdate with empty operations
graph_update = GraphUpdate(triple_operations=[])
# Generate SPARQL queries
queries = graph_update.generate_sparql_queries()
# Should generate no queries
assert len(queries) == 0
# Graph should remain unchanged
assert len(graph) == initial_triple_count
def test_graph_update_operations_with_empty_triples():
"""Test GraphUpdate with operations that have empty triples lists."""
graph = RDFGraph._from_turtle_str(
"""
@prefix ex: <http://example.org/> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
ex:Person a rdf:Class .
"""
)
initial_triple_count = len(graph)
# Create GraphUpdate with operations that have empty triples
graph_update = GraphUpdate(
triple_operations=[
TripleOp(type="insert", graph=RDFGraph()),
TripleOp(type="delete", graph=RDFGraph()),
]
)
# Generate SPARQL queries
queries = graph_update.generate_sparql_queries()
# Should generate no queries (empty triples are skipped)
assert len(queries) == 0
# Graph should remain unchanged
assert len(graph) == initial_triple_count