Remove switch to enable old graph

This commit is contained in:
Cyber MacGeddon 2026-02-16 12:56:27 +00:00
parent 2d357889a2
commit 4b3cb5a852
6 changed files with 92 additions and 173 deletions

View file

@ -463,50 +463,6 @@ class TestQuadResult:
assert result.lang == 'en' assert result.lang == 'en'
class TestFactoryFunction:
"""Test cases for get_knowledge_graph_class factory function"""
def test_factory_returns_legacy_by_default(self):
"""Test factory returns KnowledgeGraph when env var not set"""
from trustgraph.direct.cassandra_kg import get_knowledge_graph_class, KnowledgeGraph
with patch.dict(os.environ, {}, clear=True):
KGClass = get_knowledge_graph_class()
assert KGClass == KnowledgeGraph
def test_factory_returns_legacy_when_false(self):
"""Test factory returns KnowledgeGraph when env var is false"""
from trustgraph.direct.cassandra_kg import get_knowledge_graph_class, KnowledgeGraph
with patch.dict(os.environ, {'CASSANDRA_ENTITY_CENTRIC': 'false'}):
KGClass = get_knowledge_graph_class()
assert KGClass == KnowledgeGraph
def test_factory_returns_entity_centric_when_true(self):
"""Test factory returns EntityCentricKnowledgeGraph when env var is true"""
from trustgraph.direct.cassandra_kg import (
get_knowledge_graph_class, EntityCentricKnowledgeGraph
)
with patch.dict(os.environ, {'CASSANDRA_ENTITY_CENTRIC': 'true'}):
KGClass = get_knowledge_graph_class()
assert KGClass == EntityCentricKnowledgeGraph
def test_factory_case_insensitive(self):
"""Test factory handles case insensitive values"""
from trustgraph.direct.cassandra_kg import (
get_knowledge_graph_class, EntityCentricKnowledgeGraph
)
with patch.dict(os.environ, {'CASSANDRA_ENTITY_CENTRIC': 'TRUE'}):
KGClass = get_knowledge_graph_class()
assert KGClass == EntityCentricKnowledgeGraph
with patch.dict(os.environ, {'CASSANDRA_ENTITY_CENTRIC': 'True'}):
KGClass = get_knowledge_graph_class()
assert KGClass == EntityCentricKnowledgeGraph
class TestWriteHelperFunctions: class TestWriteHelperFunctions:
"""Test cases for helper functions in write.py""" """Test cases for helper functions in write.py"""

View file

@ -70,15 +70,14 @@ class TestCassandraQueryProcessor:
assert result.type == LITERAL assert result.type == LITERAL
@pytest.mark.asyncio @pytest.mark.asyncio
@patch('trustgraph.query.triples.cassandra.service.get_knowledge_graph_class') @patch('trustgraph.query.triples.cassandra.service.EntityCentricKnowledgeGraph')
async def test_query_triples_spo_query(self, mock_get_kg_class): async def test_query_triples_spo_query(self, mock_kg_class):
"""Test querying triples with subject, predicate, and object specified""" """Test querying triples with subject, predicate, and object specified"""
from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL
# Setup mock TrustGraph via factory function # Setup mock TrustGraph via factory function
mock_tg_instance = MagicMock() mock_tg_instance = MagicMock()
mock_kg_class = MagicMock(return_value=mock_tg_instance) mock_kg_class.return_value = mock_tg_instance
mock_get_kg_class.return_value = mock_kg_class
# SPO query returns a list of results (with mock graph attribute) # SPO query returns a list of results (with mock graph attribute)
mock_result = MagicMock() mock_result = MagicMock()
mock_result.g = '' mock_result.g = ''
@ -151,15 +150,14 @@ class TestCassandraQueryProcessor:
assert processor.table is None assert processor.table is None
@pytest.mark.asyncio @pytest.mark.asyncio
@patch('trustgraph.query.triples.cassandra.service.get_knowledge_graph_class') @patch('trustgraph.query.triples.cassandra.service.EntityCentricKnowledgeGraph')
async def test_query_triples_sp_pattern(self, mock_get_kg_class): async def test_query_triples_sp_pattern(self, mock_kg_class):
"""Test SP query pattern (subject and predicate, no object)""" """Test SP query pattern (subject and predicate, no object)"""
from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL
# Setup mock TrustGraph via factory function # Setup mock TrustGraph via factory function
mock_tg_instance = MagicMock() mock_tg_instance = MagicMock()
mock_kg_class = MagicMock(return_value=mock_tg_instance) mock_kg_class.return_value = mock_tg_instance
mock_get_kg_class.return_value = mock_kg_class
mock_result = MagicMock() mock_result = MagicMock()
mock_result.o = 'result_object' mock_result.o = 'result_object'
@ -189,14 +187,13 @@ class TestCassandraQueryProcessor:
assert result[0].o.value == 'result_object' assert result[0].o.value == 'result_object'
@pytest.mark.asyncio @pytest.mark.asyncio
@patch('trustgraph.query.triples.cassandra.service.get_knowledge_graph_class') @patch('trustgraph.query.triples.cassandra.service.EntityCentricKnowledgeGraph')
async def test_query_triples_s_pattern(self, mock_get_kg_class): async def test_query_triples_s_pattern(self, mock_kg_class):
"""Test S query pattern (subject only)""" """Test S query pattern (subject only)"""
from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL
mock_tg_instance = MagicMock() mock_tg_instance = MagicMock()
mock_kg_class = MagicMock(return_value=mock_tg_instance) mock_kg_class.return_value = mock_tg_instance
mock_get_kg_class.return_value = mock_kg_class
mock_result = MagicMock() mock_result = MagicMock()
mock_result.p = 'result_predicate' mock_result.p = 'result_predicate'
@ -227,14 +224,13 @@ class TestCassandraQueryProcessor:
assert result[0].o.value == 'result_object' assert result[0].o.value == 'result_object'
@pytest.mark.asyncio @pytest.mark.asyncio
@patch('trustgraph.query.triples.cassandra.service.get_knowledge_graph_class') @patch('trustgraph.query.triples.cassandra.service.EntityCentricKnowledgeGraph')
async def test_query_triples_p_pattern(self, mock_get_kg_class): async def test_query_triples_p_pattern(self, mock_kg_class):
"""Test P query pattern (predicate only)""" """Test P query pattern (predicate only)"""
from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL
mock_tg_instance = MagicMock() mock_tg_instance = MagicMock()
mock_kg_class = MagicMock(return_value=mock_tg_instance) mock_kg_class.return_value = mock_tg_instance
mock_get_kg_class.return_value = mock_kg_class
mock_result = MagicMock() mock_result = MagicMock()
mock_result.s = 'result_subject' mock_result.s = 'result_subject'
@ -265,14 +261,13 @@ class TestCassandraQueryProcessor:
assert result[0].o.value == 'result_object' assert result[0].o.value == 'result_object'
@pytest.mark.asyncio @pytest.mark.asyncio
@patch('trustgraph.query.triples.cassandra.service.get_knowledge_graph_class') @patch('trustgraph.query.triples.cassandra.service.EntityCentricKnowledgeGraph')
async def test_query_triples_o_pattern(self, mock_get_kg_class): async def test_query_triples_o_pattern(self, mock_kg_class):
"""Test O query pattern (object only)""" """Test O query pattern (object only)"""
from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL
mock_tg_instance = MagicMock() mock_tg_instance = MagicMock()
mock_kg_class = MagicMock(return_value=mock_tg_instance) mock_kg_class.return_value = mock_tg_instance
mock_get_kg_class.return_value = mock_kg_class
mock_result = MagicMock() mock_result = MagicMock()
mock_result.s = 'result_subject' mock_result.s = 'result_subject'
@ -303,14 +298,13 @@ class TestCassandraQueryProcessor:
assert result[0].o.value == 'test_object' assert result[0].o.value == 'test_object'
@pytest.mark.asyncio @pytest.mark.asyncio
@patch('trustgraph.query.triples.cassandra.service.get_knowledge_graph_class') @patch('trustgraph.query.triples.cassandra.service.EntityCentricKnowledgeGraph')
async def test_query_triples_get_all_pattern(self, mock_get_kg_class): async def test_query_triples_get_all_pattern(self, mock_kg_class):
"""Test query pattern with no constraints (get all)""" """Test query pattern with no constraints (get all)"""
from trustgraph.schema import TriplesQueryRequest from trustgraph.schema import TriplesQueryRequest
mock_tg_instance = MagicMock() mock_tg_instance = MagicMock()
mock_kg_class = MagicMock(return_value=mock_tg_instance) mock_kg_class.return_value = mock_tg_instance
mock_get_kg_class.return_value = mock_kg_class
mock_result = MagicMock() mock_result = MagicMock()
mock_result.s = 'all_subject' mock_result.s = 'all_subject'
@ -408,14 +402,13 @@ class TestCassandraQueryProcessor:
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') 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 @pytest.mark.asyncio
@patch('trustgraph.query.triples.cassandra.service.get_knowledge_graph_class') @patch('trustgraph.query.triples.cassandra.service.EntityCentricKnowledgeGraph')
async def test_query_triples_with_authentication(self, mock_get_kg_class): async def test_query_triples_with_authentication(self, mock_kg_class):
"""Test querying with username and password authentication""" """Test querying with username and password authentication"""
from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL
mock_tg_instance = MagicMock() mock_tg_instance = MagicMock()
mock_kg_class = MagicMock(return_value=mock_tg_instance) mock_kg_class.return_value = mock_tg_instance
mock_get_kg_class.return_value = mock_kg_class
# SPO query returns a list of results # SPO query returns a list of results
mock_result = MagicMock() mock_result = MagicMock()
mock_result.g = '' mock_result.g = ''
@ -451,14 +444,13 @@ class TestCassandraQueryProcessor:
) )
@pytest.mark.asyncio @pytest.mark.asyncio
@patch('trustgraph.query.triples.cassandra.service.get_knowledge_graph_class') @patch('trustgraph.query.triples.cassandra.service.EntityCentricKnowledgeGraph')
async def test_query_triples_table_reuse(self, mock_get_kg_class): async def test_query_triples_table_reuse(self, mock_kg_class):
"""Test that TrustGraph is reused for same table""" """Test that TrustGraph is reused for same table"""
from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL
mock_tg_instance = MagicMock() mock_tg_instance = MagicMock()
mock_kg_class = MagicMock(return_value=mock_tg_instance) mock_kg_class.return_value = mock_tg_instance
mock_get_kg_class.return_value = mock_kg_class
# SPO query returns a list of results # SPO query returns a list of results
mock_result = MagicMock() mock_result = MagicMock()
mock_result.g = '' mock_result.g = ''
@ -488,15 +480,14 @@ class TestCassandraQueryProcessor:
assert mock_kg_class.call_count == 1 # Should not increase assert mock_kg_class.call_count == 1 # Should not increase
@pytest.mark.asyncio @pytest.mark.asyncio
@patch('trustgraph.query.triples.cassandra.service.get_knowledge_graph_class') @patch('trustgraph.query.triples.cassandra.service.EntityCentricKnowledgeGraph')
async def test_query_triples_table_switching(self, mock_get_kg_class): async def test_query_triples_table_switching(self, mock_kg_class):
"""Test table switching creates new TrustGraph""" """Test table switching creates new TrustGraph"""
from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL
mock_tg_instance1 = MagicMock() mock_tg_instance1 = MagicMock()
mock_tg_instance2 = MagicMock() mock_tg_instance2 = MagicMock()
mock_kg_class = MagicMock(side_effect=[mock_tg_instance1, mock_tg_instance2]) mock_kg_class.side_effect = [mock_tg_instance1, mock_tg_instance2]
mock_get_kg_class.return_value = mock_kg_class
# Setup mock results for both instances # Setup mock results for both instances
mock_result = MagicMock() mock_result = MagicMock()
@ -541,14 +532,13 @@ class TestCassandraQueryProcessor:
assert mock_kg_class.call_count == 2 assert mock_kg_class.call_count == 2
@pytest.mark.asyncio @pytest.mark.asyncio
@patch('trustgraph.query.triples.cassandra.service.get_knowledge_graph_class') @patch('trustgraph.query.triples.cassandra.service.EntityCentricKnowledgeGraph')
async def test_query_triples_exception_handling(self, mock_get_kg_class): async def test_query_triples_exception_handling(self, mock_kg_class):
"""Test exception handling during query execution""" """Test exception handling during query execution"""
from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL
mock_tg_instance = MagicMock() mock_tg_instance = MagicMock()
mock_kg_class = MagicMock(return_value=mock_tg_instance) mock_kg_class.return_value = mock_tg_instance
mock_get_kg_class.return_value = mock_kg_class
mock_tg_instance.get_spo.side_effect = Exception("Query failed") mock_tg_instance.get_spo.side_effect = Exception("Query failed")
processor = Processor(taskgroup=MagicMock()) processor = Processor(taskgroup=MagicMock())
@ -566,14 +556,13 @@ class TestCassandraQueryProcessor:
await processor.query_triples(query) await processor.query_triples(query)
@pytest.mark.asyncio @pytest.mark.asyncio
@patch('trustgraph.query.triples.cassandra.service.get_knowledge_graph_class') @patch('trustgraph.query.triples.cassandra.service.EntityCentricKnowledgeGraph')
async def test_query_triples_multiple_results(self, mock_get_kg_class): async def test_query_triples_multiple_results(self, mock_kg_class):
"""Test query returning multiple results""" """Test query returning multiple results"""
from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL
mock_tg_instance = MagicMock() mock_tg_instance = MagicMock()
mock_kg_class = MagicMock(return_value=mock_tg_instance) mock_kg_class.return_value = mock_tg_instance
mock_get_kg_class.return_value = mock_kg_class
# Mock multiple results # Mock multiple results
mock_result1 = MagicMock() mock_result1 = MagicMock()
@ -612,14 +601,13 @@ class TestCassandraQueryPerformanceOptimizations:
"""Test cases for multi-table performance optimizations in query service""" """Test cases for multi-table performance optimizations in query service"""
@pytest.mark.asyncio @pytest.mark.asyncio
@patch('trustgraph.query.triples.cassandra.service.get_knowledge_graph_class') @patch('trustgraph.query.triples.cassandra.service.EntityCentricKnowledgeGraph')
async def test_get_po_query_optimization(self, mock_get_kg_class): async def test_get_po_query_optimization(self, mock_kg_class):
"""Test that get_po queries use optimized table (no ALLOW FILTERING)""" """Test that get_po queries use optimized table (no ALLOW FILTERING)"""
from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL
mock_tg_instance = MagicMock() mock_tg_instance = MagicMock()
mock_kg_class = MagicMock(return_value=mock_tg_instance) mock_kg_class.return_value = mock_tg_instance
mock_get_kg_class.return_value = mock_kg_class
mock_result = MagicMock() mock_result = MagicMock()
mock_result.s = 'result_subject' mock_result.s = 'result_subject'
@ -654,14 +642,13 @@ class TestCassandraQueryPerformanceOptimizations:
assert result[0].o.value == 'test_object' assert result[0].o.value == 'test_object'
@pytest.mark.asyncio @pytest.mark.asyncio
@patch('trustgraph.query.triples.cassandra.service.get_knowledge_graph_class') @patch('trustgraph.query.triples.cassandra.service.EntityCentricKnowledgeGraph')
async def test_get_os_query_optimization(self, mock_get_kg_class): async def test_get_os_query_optimization(self, mock_kg_class):
"""Test that get_os queries use optimized table (no ALLOW FILTERING)""" """Test that get_os queries use optimized table (no ALLOW FILTERING)"""
from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL
mock_tg_instance = MagicMock() mock_tg_instance = MagicMock()
mock_kg_class = MagicMock(return_value=mock_tg_instance) mock_kg_class.return_value = mock_tg_instance
mock_get_kg_class.return_value = mock_kg_class
mock_result = MagicMock() mock_result = MagicMock()
mock_result.p = 'result_predicate' mock_result.p = 'result_predicate'
@ -696,14 +683,13 @@ class TestCassandraQueryPerformanceOptimizations:
assert result[0].o.value == 'test_object' assert result[0].o.value == 'test_object'
@pytest.mark.asyncio @pytest.mark.asyncio
@patch('trustgraph.query.triples.cassandra.service.get_knowledge_graph_class') @patch('trustgraph.query.triples.cassandra.service.EntityCentricKnowledgeGraph')
async def test_all_query_patterns_use_correct_tables(self, mock_get_kg_class): async def test_all_query_patterns_use_correct_tables(self, mock_kg_class):
"""Test that all query patterns route to their optimal tables""" """Test that all query patterns route to their optimal tables"""
from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL
mock_tg_instance = MagicMock() mock_tg_instance = MagicMock()
mock_kg_class = MagicMock(return_value=mock_tg_instance) mock_kg_class.return_value = mock_tg_instance
mock_get_kg_class.return_value = mock_kg_class
# Mock empty results for all queries # Mock empty results for all queries
mock_tg_instance.get_all.return_value = [] mock_tg_instance.get_all.return_value = []
@ -769,14 +755,13 @@ class TestCassandraQueryPerformanceOptimizations:
# Mode is determined in KnowledgeGraph initialization # Mode is determined in KnowledgeGraph initialization
@pytest.mark.asyncio @pytest.mark.asyncio
@patch('trustgraph.query.triples.cassandra.service.get_knowledge_graph_class') @patch('trustgraph.query.triples.cassandra.service.EntityCentricKnowledgeGraph')
async def test_performance_critical_po_query_no_filtering(self, mock_get_kg_class): async def test_performance_critical_po_query_no_filtering(self, mock_kg_class):
"""Test the performance-critical PO query that eliminates ALLOW FILTERING""" """Test the performance-critical PO query that eliminates ALLOW FILTERING"""
from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL from trustgraph.schema import TriplesQueryRequest, Term, IRI, LITERAL
mock_tg_instance = MagicMock() mock_tg_instance = MagicMock()
mock_kg_class = MagicMock(return_value=mock_tg_instance) mock_kg_class.return_value = mock_tg_instance
mock_get_kg_class.return_value = mock_kg_class
# Mock multiple subjects for the same predicate-object pair # Mock multiple subjects for the same predicate-object pair
mock_results = [] mock_results = []

View file

@ -87,13 +87,12 @@ class TestCassandraStorageProcessor:
assert processor.cassandra_username == 'new-user' # Only cassandra_* params work assert processor.cassandra_username == 'new-user' # Only cassandra_* params work
@pytest.mark.asyncio @pytest.mark.asyncio
@patch('trustgraph.storage.triples.cassandra.write.get_knowledge_graph_class') @patch('trustgraph.storage.triples.cassandra.write.EntityCentricKnowledgeGraph')
async def test_table_switching_with_auth(self, mock_get_kg_class): async def test_table_switching_with_auth(self, mock_kg_class):
"""Test table switching logic when authentication is provided""" """Test table switching logic when authentication is provided"""
taskgroup_mock = MagicMock() taskgroup_mock = MagicMock()
mock_tg_instance = MagicMock() mock_tg_instance = MagicMock()
mock_kg_class = MagicMock(return_value=mock_tg_instance) mock_kg_class.return_value = mock_tg_instance
mock_get_kg_class.return_value = mock_kg_class
processor = Processor( processor = Processor(
taskgroup=taskgroup_mock, taskgroup=taskgroup_mock,
@ -119,13 +118,12 @@ class TestCassandraStorageProcessor:
assert processor.table == 'user1' assert processor.table == 'user1'
@pytest.mark.asyncio @pytest.mark.asyncio
@patch('trustgraph.storage.triples.cassandra.write.get_knowledge_graph_class') @patch('trustgraph.storage.triples.cassandra.write.EntityCentricKnowledgeGraph')
async def test_table_switching_without_auth(self, mock_get_kg_class): async def test_table_switching_without_auth(self, mock_kg_class):
"""Test table switching logic when no authentication is provided""" """Test table switching logic when no authentication is provided"""
taskgroup_mock = MagicMock() taskgroup_mock = MagicMock()
mock_tg_instance = MagicMock() mock_tg_instance = MagicMock()
mock_kg_class = MagicMock(return_value=mock_tg_instance) mock_kg_class.return_value = mock_tg_instance
mock_get_kg_class.return_value = mock_kg_class
processor = Processor(taskgroup=taskgroup_mock) processor = Processor(taskgroup=taskgroup_mock)
@ -145,13 +143,12 @@ class TestCassandraStorageProcessor:
assert processor.table == 'user2' assert processor.table == 'user2'
@pytest.mark.asyncio @pytest.mark.asyncio
@patch('trustgraph.storage.triples.cassandra.write.get_knowledge_graph_class') @patch('trustgraph.storage.triples.cassandra.write.EntityCentricKnowledgeGraph')
async def test_table_reuse_when_same(self, mock_get_kg_class): async def test_table_reuse_when_same(self, mock_kg_class):
"""Test that TrustGraph is not recreated when table hasn't changed""" """Test that TrustGraph is not recreated when table hasn't changed"""
taskgroup_mock = MagicMock() taskgroup_mock = MagicMock()
mock_tg_instance = MagicMock() mock_tg_instance = MagicMock()
mock_kg_class = MagicMock(return_value=mock_tg_instance) mock_kg_class.return_value = mock_tg_instance
mock_get_kg_class.return_value = mock_kg_class
processor = Processor(taskgroup=taskgroup_mock) processor = Processor(taskgroup=taskgroup_mock)
@ -170,13 +167,12 @@ class TestCassandraStorageProcessor:
assert mock_kg_class.call_count == 1 # Should not increase assert mock_kg_class.call_count == 1 # Should not increase
@pytest.mark.asyncio @pytest.mark.asyncio
@patch('trustgraph.storage.triples.cassandra.write.get_knowledge_graph_class') @patch('trustgraph.storage.triples.cassandra.write.EntityCentricKnowledgeGraph')
async def test_triple_insertion(self, mock_get_kg_class): async def test_triple_insertion(self, mock_kg_class):
"""Test that triples are properly inserted into Cassandra""" """Test that triples are properly inserted into Cassandra"""
taskgroup_mock = MagicMock() taskgroup_mock = MagicMock()
mock_tg_instance = MagicMock() mock_tg_instance = MagicMock()
mock_kg_class = MagicMock(return_value=mock_tg_instance) mock_kg_class.return_value = mock_tg_instance
mock_get_kg_class.return_value = mock_kg_class
processor = Processor(taskgroup=taskgroup_mock) processor = Processor(taskgroup=taskgroup_mock)
@ -227,13 +223,12 @@ class TestCassandraStorageProcessor:
) )
@pytest.mark.asyncio @pytest.mark.asyncio
@patch('trustgraph.storage.triples.cassandra.write.get_knowledge_graph_class') @patch('trustgraph.storage.triples.cassandra.write.EntityCentricKnowledgeGraph')
async def test_triple_insertion_with_empty_list(self, mock_get_kg_class): async def test_triple_insertion_with_empty_list(self, mock_kg_class):
"""Test behavior when message has no triples""" """Test behavior when message has no triples"""
taskgroup_mock = MagicMock() taskgroup_mock = MagicMock()
mock_tg_instance = MagicMock() mock_tg_instance = MagicMock()
mock_kg_class = MagicMock(return_value=mock_tg_instance) mock_kg_class.return_value = mock_tg_instance
mock_get_kg_class.return_value = mock_kg_class
processor = Processor(taskgroup=taskgroup_mock) processor = Processor(taskgroup=taskgroup_mock)
@ -249,13 +244,12 @@ class TestCassandraStorageProcessor:
mock_tg_instance.insert.assert_not_called() mock_tg_instance.insert.assert_not_called()
@pytest.mark.asyncio @pytest.mark.asyncio
@patch('trustgraph.storage.triples.cassandra.write.get_knowledge_graph_class') @patch('trustgraph.storage.triples.cassandra.write.EntityCentricKnowledgeGraph')
@patch('trustgraph.storage.triples.cassandra.write.time.sleep') @patch('trustgraph.storage.triples.cassandra.write.time.sleep')
async def test_exception_handling_with_retry(self, mock_sleep, mock_get_kg_class): async def test_exception_handling_with_retry(self, mock_sleep, mock_kg_class):
"""Test exception handling during TrustGraph creation""" """Test exception handling during TrustGraph creation"""
taskgroup_mock = MagicMock() taskgroup_mock = MagicMock()
mock_kg_class = MagicMock(side_effect=Exception("Connection failed")) mock_kg_class.side_effect = Exception("Connection failed")
mock_get_kg_class.return_value = mock_kg_class
processor = Processor(taskgroup=taskgroup_mock) processor = Processor(taskgroup=taskgroup_mock)
@ -355,14 +349,13 @@ class TestCassandraStorageProcessor:
mock_launch.assert_called_once_with(default_ident, '\nGraph writer. Input is graph edge. Writes edges to Cassandra graph.\n') mock_launch.assert_called_once_with(default_ident, '\nGraph writer. Input is graph edge. Writes edges to Cassandra graph.\n')
@pytest.mark.asyncio @pytest.mark.asyncio
@patch('trustgraph.storage.triples.cassandra.write.get_knowledge_graph_class') @patch('trustgraph.storage.triples.cassandra.write.EntityCentricKnowledgeGraph')
async def test_store_triples_table_switching_between_different_tables(self, mock_get_kg_class): async def test_store_triples_table_switching_between_different_tables(self, mock_kg_class):
"""Test table switching when different tables are used in sequence""" """Test table switching when different tables are used in sequence"""
taskgroup_mock = MagicMock() taskgroup_mock = MagicMock()
mock_tg_instance1 = MagicMock() mock_tg_instance1 = MagicMock()
mock_tg_instance2 = MagicMock() mock_tg_instance2 = MagicMock()
mock_kg_class = MagicMock(side_effect=[mock_tg_instance1, mock_tg_instance2]) mock_kg_class.side_effect = [mock_tg_instance1, mock_tg_instance2]
mock_get_kg_class.return_value = mock_kg_class
processor = Processor(taskgroup=taskgroup_mock) processor = Processor(taskgroup=taskgroup_mock)
@ -390,13 +383,12 @@ class TestCassandraStorageProcessor:
assert mock_kg_class.call_count == 2 assert mock_kg_class.call_count == 2
@pytest.mark.asyncio @pytest.mark.asyncio
@patch('trustgraph.storage.triples.cassandra.write.get_knowledge_graph_class') @patch('trustgraph.storage.triples.cassandra.write.EntityCentricKnowledgeGraph')
async def test_store_triples_with_special_characters_in_values(self, mock_get_kg_class): async def test_store_triples_with_special_characters_in_values(self, mock_kg_class):
"""Test storing triples with special characters and unicode""" """Test storing triples with special characters and unicode"""
taskgroup_mock = MagicMock() taskgroup_mock = MagicMock()
mock_tg_instance = MagicMock() mock_tg_instance = MagicMock()
mock_kg_class = MagicMock(return_value=mock_tg_instance) mock_kg_class.return_value = mock_tg_instance
mock_get_kg_class.return_value = mock_kg_class
processor = Processor(taskgroup=taskgroup_mock) processor = Processor(taskgroup=taskgroup_mock)
@ -434,8 +426,8 @@ class TestCassandraStorageProcessor:
) )
@pytest.mark.asyncio @pytest.mark.asyncio
@patch('trustgraph.storage.triples.cassandra.write.get_knowledge_graph_class') @patch('trustgraph.storage.triples.cassandra.write.EntityCentricKnowledgeGraph')
async def test_store_triples_preserves_old_table_on_exception(self, mock_get_kg_class): async def test_store_triples_preserves_old_table_on_exception(self, mock_kg_class):
"""Test that table remains unchanged when TrustGraph creation fails""" """Test that table remains unchanged when TrustGraph creation fails"""
taskgroup_mock = MagicMock() taskgroup_mock = MagicMock()
@ -445,8 +437,7 @@ class TestCassandraStorageProcessor:
processor.table = ('old_user', 'old_collection') processor.table = ('old_user', 'old_collection')
# Mock TrustGraph to raise exception # Mock TrustGraph to raise exception
mock_kg_class = MagicMock(side_effect=Exception("Connection failed")) mock_kg_class.side_effect = Exception("Connection failed")
mock_get_kg_class.return_value = mock_kg_class
mock_message = MagicMock() mock_message = MagicMock()
mock_message.metadata.user = 'new_user' mock_message.metadata.user = 'new_user'
@ -466,13 +457,12 @@ class TestCassandraPerformanceOptimizations:
"""Test cases for multi-table performance optimizations""" """Test cases for multi-table performance optimizations"""
@pytest.mark.asyncio @pytest.mark.asyncio
@patch('trustgraph.storage.triples.cassandra.write.get_knowledge_graph_class') @patch('trustgraph.storage.triples.cassandra.write.EntityCentricKnowledgeGraph')
async def test_legacy_mode_uses_single_table(self, mock_get_kg_class): async def test_legacy_mode_uses_single_table(self, mock_kg_class):
"""Test that legacy mode still works with single table""" """Test that legacy mode still works with single table"""
taskgroup_mock = MagicMock() taskgroup_mock = MagicMock()
mock_tg_instance = MagicMock() mock_tg_instance = MagicMock()
mock_kg_class = MagicMock(return_value=mock_tg_instance) mock_kg_class.return_value = mock_tg_instance
mock_get_kg_class.return_value = mock_kg_class
with patch.dict('os.environ', {'CASSANDRA_USE_LEGACY': 'true'}): with patch.dict('os.environ', {'CASSANDRA_USE_LEGACY': 'true'}):
processor = Processor(taskgroup=taskgroup_mock) processor = Processor(taskgroup=taskgroup_mock)
@ -488,13 +478,12 @@ class TestCassandraPerformanceOptimizations:
assert mock_tg_instance is not None assert mock_tg_instance is not None
@pytest.mark.asyncio @pytest.mark.asyncio
@patch('trustgraph.storage.triples.cassandra.write.get_knowledge_graph_class') @patch('trustgraph.storage.triples.cassandra.write.EntityCentricKnowledgeGraph')
async def test_optimized_mode_uses_multi_table(self, mock_get_kg_class): async def test_optimized_mode_uses_multi_table(self, mock_kg_class):
"""Test that optimized mode uses multi-table schema""" """Test that optimized mode uses multi-table schema"""
taskgroup_mock = MagicMock() taskgroup_mock = MagicMock()
mock_tg_instance = MagicMock() mock_tg_instance = MagicMock()
mock_kg_class = MagicMock(return_value=mock_tg_instance) mock_kg_class.return_value = mock_tg_instance
mock_get_kg_class.return_value = mock_kg_class
with patch.dict('os.environ', {'CASSANDRA_USE_LEGACY': 'false'}): with patch.dict('os.environ', {'CASSANDRA_USE_LEGACY': 'false'}):
processor = Processor(taskgroup=taskgroup_mock) processor = Processor(taskgroup=taskgroup_mock)
@ -510,13 +499,12 @@ class TestCassandraPerformanceOptimizations:
assert mock_tg_instance is not None assert mock_tg_instance is not None
@pytest.mark.asyncio @pytest.mark.asyncio
@patch('trustgraph.storage.triples.cassandra.write.get_knowledge_graph_class') @patch('trustgraph.storage.triples.cassandra.write.EntityCentricKnowledgeGraph')
async def test_batch_write_consistency(self, mock_get_kg_class): async def test_batch_write_consistency(self, mock_kg_class):
"""Test that all tables stay consistent during batch writes""" """Test that all tables stay consistent during batch writes"""
taskgroup_mock = MagicMock() taskgroup_mock = MagicMock()
mock_tg_instance = MagicMock() mock_tg_instance = MagicMock()
mock_kg_class = MagicMock(return_value=mock_tg_instance) mock_kg_class.return_value = mock_tg_instance
mock_get_kg_class.return_value = mock_kg_class
processor = Processor(taskgroup=taskgroup_mock) processor = Processor(taskgroup=taskgroup_mock)

View file

@ -20,6 +20,11 @@ DEFAULT_GRAPH = ""
class KnowledgeGraph: class KnowledgeGraph:
""" """
REDUNDANT: This 7-table implementation has been superseded by
EntityCentricKnowledgeGraph which uses a more efficient 2-table model.
This class is retained temporarily for reference but should not be used
for new deployments.
Cassandra-backed knowledge graph supporting quads (s, p, o, g). Cassandra-backed knowledge graph supporting quads (s, p, o, g).
Uses 7 tables to support all 16 query patterns efficiently: Uses 7 tables to support all 16 query patterns efficiently:
@ -1088,18 +1093,3 @@ class QuadResult:
self.lang = lang self.lang = lang
def get_knowledge_graph_class():
"""
Factory function to select KnowledgeGraph implementation.
Uses CASSANDRA_ENTITY_CENTRIC environment variable to select:
- "true": EntityCentricKnowledgeGraph (new 2-table model)
- Otherwise: KnowledgeGraph (original 7-table model)
"""
use_entity_centric = os.environ.get("CASSANDRA_ENTITY_CENTRIC", "").lower() == "true"
if use_entity_centric:
logger.info("Using EntityCentricKnowledgeGraph (2-table model)")
return EntityCentricKnowledgeGraph
else:
logger.info("Using KnowledgeGraph (7-table model)")
return KnowledgeGraph

View file

@ -7,7 +7,7 @@ null. Output is a list of quads.
import logging import logging
from .... direct.cassandra_kg import ( from .... direct.cassandra_kg import (
KnowledgeGraph, GRAPH_WILDCARD, DEFAULT_GRAPH, get_knowledge_graph_class EntityCentricKnowledgeGraph, GRAPH_WILDCARD, DEFAULT_GRAPH
) )
from .... schema import TriplesQueryRequest, TriplesQueryResponse, Error from .... schema import TriplesQueryRequest, TriplesQueryResponse, Error
from .... schema import Term, Triple, IRI, LITERAL from .... schema import Term, Triple, IRI, LITERAL
@ -106,7 +106,7 @@ class Processor(TriplesQueryService):
if user != self.table: if user != self.table:
# Use factory function to select implementation # Use factory function to select implementation
KGClass = get_knowledge_graph_class() KGClass = EntityCentricKnowledgeGraph
if self.cassandra_username and self.cassandra_password: if self.cassandra_username and self.cassandra_password:
self.tg = KGClass( self.tg = KGClass(

View file

@ -11,7 +11,7 @@ import time
import logging import logging
from .... direct.cassandra_kg import ( from .... direct.cassandra_kg import (
KnowledgeGraph, DEFAULT_GRAPH, get_knowledge_graph_class EntityCentricKnowledgeGraph, DEFAULT_GRAPH
) )
from .... base import TriplesStoreService, CollectionConfigHandler from .... base import TriplesStoreService, CollectionConfigHandler
from .... base import AsyncProcessor, Consumer, Producer from .... base import AsyncProcessor, Consumer, Producer
@ -121,7 +121,7 @@ class Processor(CollectionConfigHandler, TriplesStoreService):
self.tg = None self.tg = None
# Use factory function to select implementation # Use factory function to select implementation
KGClass = get_knowledge_graph_class() KGClass = EntityCentricKnowledgeGraph
try: try:
if self.cassandra_username and self.cassandra_password: if self.cassandra_username and self.cassandra_password:
@ -174,7 +174,7 @@ class Processor(CollectionConfigHandler, TriplesStoreService):
self.tg = None self.tg = None
# Use factory function to select implementation # Use factory function to select implementation
KGClass = get_knowledge_graph_class() KGClass = EntityCentricKnowledgeGraph
try: try:
if self.cassandra_username and self.cassandra_password: if self.cassandra_username and self.cassandra_password:
@ -216,7 +216,7 @@ class Processor(CollectionConfigHandler, TriplesStoreService):
self.tg = None self.tg = None
# Use factory function to select implementation # Use factory function to select implementation
KGClass = get_knowledge_graph_class() KGClass = EntityCentricKnowledgeGraph
try: try:
if self.cassandra_username and self.cassandra_password: if self.cassandra_username and self.cassandra_password: