diff --git a/tests/integration/test_rows_cassandra_integration.py b/tests/integration/test_rows_cassandra_integration.py index 9067816a..a2b8ae08 100644 --- a/tests/integration/test_rows_cassandra_integration.py +++ b/tests/integration/test_rows_cassandra_integration.py @@ -18,6 +18,20 @@ from trustgraph.schema import ExtractedObject, Metadata, RowSchema, Field class TestRowsCassandraIntegration: """Integration tests for Cassandra row storage with unified table""" + @pytest.fixture(autouse=True) + def patch_async_execute(self): + """Route async_execute through session.execute so the mock's + side_effect handles all CQL (DDL and DML) uniformly and every + call lands in mock_session.execute.call_args_list.""" + async def _fake(session, query, params=None): + session.execute(query, params) + return [] + with patch( + 'trustgraph.storage.rows.cassandra.write.async_execute', + new=_fake, + ): + yield + @pytest.fixture def mock_cassandra_session(self): """Mock Cassandra session for integration tests""" diff --git a/tests/pytest.ini b/tests/pytest.ini index 8541bd8f..5dcc095c 100644 --- a/tests/pytest.ini +++ b/tests/pytest.ini @@ -1,6 +1,5 @@ [pytest] testpaths = tests -python_paths = . python_files = test_*.py python_classes = Test* python_functions = test_* @@ -8,7 +7,7 @@ addopts = -v --tb=short --strict-markers - --disable-warnings +# --disable-warnings # --cov-fail-under=80 asyncio_mode = auto markers = diff --git a/tests/unit/test_agent/test_tool_coordination.py b/tests/unit/test_agent/test_tool_coordination.py index e53416f7..6bb9b8fa 100644 --- a/tests/unit/test_agent/test_tool_coordination.py +++ b/tests/unit/test_agent/test_tool_coordination.py @@ -9,6 +9,7 @@ tool usage patterns. import pytest from unittest.mock import Mock, AsyncMock import asyncio +import inspect from collections import defaultdict @@ -133,7 +134,7 @@ class TestToolCoordinationLogic: resolved_params[key] = value # Execute tool - if asyncio.iscoroutinefunction(tool_function): + if inspect.iscoroutinefunction(tool_function): result = await tool_function(**resolved_params) else: result = tool_function(**resolved_params) @@ -227,7 +228,7 @@ class TestToolCoordinationLogic: # Simulate async execution with delay await asyncio.sleep(0.001) # Small delay to simulate work - if asyncio.iscoroutinefunction(tool_function): + if inspect.iscoroutinefunction(tool_function): result = await tool_function(**parameters) else: result = tool_function(**parameters) @@ -337,7 +338,7 @@ class TestToolCoordinationLogic: if attempt > 0: await asyncio.sleep(0.001 * (self.backoff_factor ** attempt)) - if asyncio.iscoroutinefunction(tool_function): + if inspect.iscoroutinefunction(tool_function): result = await tool_function(**parameters) else: result = tool_function(**parameters) diff --git a/tests/unit/test_base/test_logging.py b/tests/unit/test_base/test_logging.py index 13a2718c..67c465a5 100644 --- a/tests/unit/test_base/test_logging.py +++ b/tests/unit/test_base/test_logging.py @@ -45,7 +45,7 @@ def test_setup_logging_without_loki_configures_console(monkeypatch): kwargs = basic_config.call_args.kwargs assert kwargs["level"] == logging.DEBUG assert kwargs["force"] is True - assert "processor-1" in kwargs["format"] + assert "%(processor_id)s" in kwargs["format"] assert len(kwargs["handlers"]) == 1 logger.info.assert_called_once_with("Logging configured with level: debug") @@ -60,11 +60,14 @@ def test_setup_logging_with_loki_enables_queue_listener(monkeypatch): queue_listener = MagicMock() loki_handler = MagicMock() + noisy_logger = MagicMock() logger_map = { None: root_logger, "trustgraph.base.logging": module_logger, "urllib3": urllib3_logger, "urllib3.connectionpool": connectionpool_logger, + "pika": noisy_logger, + "cassandra": noisy_logger, } monkeypatch.setattr(logging, "basicConfig", basic_config) diff --git a/tests/unit/test_query/test_rows_cassandra_query.py b/tests/unit/test_query/test_rows_cassandra_query.py index 879a81c5..c0d399c3 100644 --- a/tests/unit/test_query/test_rows_cassandra_query.py +++ b/tests/unit/test_query/test_rows_cassandra_query.py @@ -330,7 +330,8 @@ class TestUnifiedTableQueries: """Test queries against the unified rows table""" @pytest.mark.asyncio - async def test_query_with_index_match(self): + @patch('trustgraph.query.rows.cassandra.service.async_execute', new_callable=AsyncMock) + async def test_query_with_index_match(self, mock_async_execute): """Test query execution with matching index""" processor = MagicMock() processor.session = MagicMock() @@ -340,10 +341,10 @@ class TestUnifiedTableQueries: processor.find_matching_index = Processor.find_matching_index.__get__(processor, Processor) processor.query_cassandra = Processor.query_cassandra.__get__(processor, Processor) - # Mock session execute to return test data + # Mock async_execute to return test data mock_row = MagicMock() mock_row.data = {"id": "123", "name": "Test Product", "category": "electronics"} - processor.session.execute.return_value = [mock_row] + mock_async_execute.return_value = [mock_row] schema = RowSchema( name="products", @@ -366,12 +367,12 @@ class TestUnifiedTableQueries: # Verify Cassandra was connected and queried processor.connect_cassandra.assert_called_once() - processor.session.execute.assert_called_once() + mock_async_execute.assert_called_once() # Verify query structure - should query unified rows table - call_args = processor.session.execute.call_args - query = call_args[0][0] - params = call_args[0][1] + call_args = mock_async_execute.call_args + query = call_args[0][1] + params = call_args[0][2] assert "SELECT data, source FROM test_user.rows" in query assert "collection = %s" in query @@ -390,7 +391,8 @@ class TestUnifiedTableQueries: assert results[0]["category"] == "electronics" @pytest.mark.asyncio - async def test_query_without_index_match(self): + @patch('trustgraph.query.rows.cassandra.service.async_execute', new_callable=AsyncMock) + async def test_query_without_index_match(self, mock_async_execute): """Test query execution without matching index (scan mode)""" processor = MagicMock() processor.session = MagicMock() @@ -401,12 +403,12 @@ class TestUnifiedTableQueries: processor._matches_filters = Processor._matches_filters.__get__(processor, Processor) processor.query_cassandra = Processor.query_cassandra.__get__(processor, Processor) - # Mock session execute to return test data + # Mock async_execute to return test data mock_row1 = MagicMock() mock_row1.data = {"id": "1", "name": "Product A", "price": "100"} mock_row2 = MagicMock() mock_row2.data = {"id": "2", "name": "Product B", "price": "200"} - processor.session.execute.return_value = [mock_row1, mock_row2] + mock_async_execute.return_value = [mock_row1, mock_row2] schema = RowSchema( name="products", @@ -428,8 +430,8 @@ class TestUnifiedTableQueries: ) # Query should use ALLOW FILTERING for scan - call_args = processor.session.execute.call_args - query = call_args[0][0] + call_args = mock_async_execute.call_args + query = call_args[0][1] assert "ALLOW FILTERING" in query diff --git a/tests/unit/test_retrieval/test_nlp_query.py b/tests/unit/test_retrieval/test_nlp_query.py index 5141f2b2..1fd35c2e 100644 --- a/tests/unit/test_retrieval/test_nlp_query.py +++ b/tests/unit/test_retrieval/test_nlp_query.py @@ -72,7 +72,6 @@ def processor(mock_pulsar_client, sample_schemas): return proc -@pytest.mark.asyncio class TestNLPQueryProcessor: """Test NLP Query service processor""" diff --git a/tests/unit/test_retrieval/test_structured_query.py b/tests/unit/test_retrieval/test_structured_query.py index 76bf5b08..9a183f45 100644 --- a/tests/unit/test_retrieval/test_structured_query.py +++ b/tests/unit/test_retrieval/test_structured_query.py @@ -36,7 +36,6 @@ def processor(mock_pulsar_client): return proc -@pytest.mark.asyncio class TestStructuredQueryProcessor: """Test Structured Query service processor""" diff --git a/tests/unit/test_storage/test_rows_cassandra_storage.py b/tests/unit/test_storage/test_rows_cassandra_storage.py index 1976b844..ccf193aa 100644 --- a/tests/unit/test_storage/test_rows_cassandra_storage.py +++ b/tests/unit/test_storage/test_rows_cassandra_storage.py @@ -160,7 +160,8 @@ class TestRowsCassandraStorageLogic: assert id_field.primary is True @pytest.mark.asyncio - async def test_object_processing_stores_data_map(self): + @patch('trustgraph.storage.rows.cassandra.write.async_execute', new_callable=AsyncMock) + async def test_object_processing_stores_data_map(self, mock_async_execute): """Test that row processing stores data as map""" processor = MagicMock() processor.schemas = { @@ -184,6 +185,8 @@ class TestRowsCassandraStorageLogic: processor.collection_exists = MagicMock(return_value=True) processor.on_object = Processor.on_object.__get__(processor, Processor) + mock_async_execute.return_value = [] + # Create test object test_obj = ExtractedObject( metadata=Metadata( @@ -205,10 +208,10 @@ class TestRowsCassandraStorageLogic: await processor.on_object(msg, None, None) # Verify insert was executed - processor.session.execute.assert_called() - insert_call = processor.session.execute.call_args - insert_cql = insert_call[0][0] - values = insert_call[0][1] + mock_async_execute.assert_called() + insert_call = mock_async_execute.call_args + insert_cql = insert_call[0][1] + values = insert_call[0][2] # Verify using unified rows table assert "INSERT INTO test_user.rows" in insert_cql @@ -222,7 +225,8 @@ class TestRowsCassandraStorageLogic: assert values[5] == "" # source @pytest.mark.asyncio - async def test_object_processing_multiple_indexes(self): + @patch('trustgraph.storage.rows.cassandra.write.async_execute', new_callable=AsyncMock) + async def test_object_processing_multiple_indexes(self, mock_async_execute): """Test that row is written once per indexed field""" processor = MagicMock() processor.schemas = { @@ -246,6 +250,8 @@ class TestRowsCassandraStorageLogic: processor.collection_exists = MagicMock(return_value=True) processor.on_object = Processor.on_object.__get__(processor, Processor) + mock_async_execute.return_value = [] + test_obj = ExtractedObject( metadata=Metadata( id="test-001", @@ -264,12 +270,12 @@ class TestRowsCassandraStorageLogic: await processor.on_object(msg, None, None) # Should have 3 inserts (one per indexed field: id, category, status) - assert processor.session.execute.call_count == 3 + assert mock_async_execute.call_count == 3 # Check that different index_names were used index_names_used = set() - for call in processor.session.execute.call_args_list: - values = call[0][1] + for call in mock_async_execute.call_args_list: + values = call[0][2] index_names_used.add(values[2]) # index_name is 3rd value assert index_names_used == {"id", "category", "status"} @@ -279,7 +285,8 @@ class TestRowsCassandraStorageBatchLogic: """Test batch processing logic for unified table implementation""" @pytest.mark.asyncio - async def test_batch_object_processing(self): + @patch('trustgraph.storage.rows.cassandra.write.async_execute', new_callable=AsyncMock) + async def test_batch_object_processing(self, mock_async_execute): """Test processing of batch ExtractedObjects""" processor = MagicMock() processor.schemas = { @@ -302,6 +309,8 @@ class TestRowsCassandraStorageBatchLogic: processor.collection_exists = MagicMock(return_value=True) processor.on_object = Processor.on_object.__get__(processor, Processor) + mock_async_execute.return_value = [] + # Create batch object with multiple values batch_obj = ExtractedObject( metadata=Metadata( @@ -325,12 +334,12 @@ class TestRowsCassandraStorageBatchLogic: await processor.on_object(msg, None, None) # Should have 3 inserts (one per row, one index per row since only primary key) - assert processor.session.execute.call_count == 3 + assert mock_async_execute.call_count == 3 # Check each insert has different id ids_inserted = set() - for call in processor.session.execute.call_args_list: - values = call[0][1] + for call in mock_async_execute.call_args_list: + values = call[0][2] ids_inserted.add(tuple(values[3])) # index_value is 4th value assert ids_inserted == {("001",), ("002",), ("003",)} diff --git a/tests/unit/test_tables/test_knowledge_table_store.py b/tests/unit/test_tables/test_knowledge_table_store.py index 5129b01e..4ab0ffeb 100644 --- a/tests/unit/test_tables/test_knowledge_table_store.py +++ b/tests/unit/test_tables/test_knowledge_table_store.py @@ -9,7 +9,7 @@ with hand-built fake rows. """ import pytest -from unittest.mock import Mock +from unittest.mock import Mock, AsyncMock, patch from trustgraph.tables.knowledge import KnowledgeTableStore from trustgraph.schema import ( @@ -35,7 +35,10 @@ def _make_store(): class TestGetGraphEmbeddings: @pytest.mark.asyncio - async def test_row_converts_to_entity_embeddings_with_singular_vector(self): + @patch('trustgraph.tables.knowledge.async_execute', new_callable=AsyncMock) + async def test_row_converts_to_entity_embeddings_with_singular_vector( + self, mock_async_execute + ): """ Cassandra rows return entities as a list of [entity_tuple, vector] pairs in row[3]. The deserializer must construct EntityEmbeddings @@ -56,8 +59,8 @@ class TestGetGraphEmbeddings: store = _make_store() store.cassandra = Mock() - store.cassandra.execute = Mock(return_value=[fake_row]) store.get_graph_embeddings_stmt = Mock() + mock_async_execute.return_value = [fake_row] received = [] @@ -72,7 +75,8 @@ class TestGetGraphEmbeddings: ) # Assert - store.cassandra.execute.assert_called_once_with( + mock_async_execute.assert_called_once_with( + store.cassandra, store.get_graph_embeddings_stmt, ("alice", "doc-1"), ) @@ -102,15 +106,16 @@ class TestGetGraphEmbeddings: assert ge.entities[2].entity.value == "a literal entity" @pytest.mark.asyncio - async def test_empty_entities_blob_yields_empty_list(self): + @patch('trustgraph.tables.knowledge.async_execute', new_callable=AsyncMock) + async def test_empty_entities_blob_yields_empty_list(self, mock_async_execute): """row[3] being None / empty must produce a GraphEmbeddings with no entities, not raise.""" fake_row = (None, None, None, None) store = _make_store() store.cassandra = Mock() - store.cassandra.execute = Mock(return_value=[fake_row]) store.get_graph_embeddings_stmt = Mock() + mock_async_execute.return_value = [fake_row] received = [] @@ -123,7 +128,8 @@ class TestGetGraphEmbeddings: assert received[0].entities == [] @pytest.mark.asyncio - async def test_multiple_rows_each_emit_one_message(self): + @patch('trustgraph.tables.knowledge.async_execute', new_callable=AsyncMock) + async def test_multiple_rows_each_emit_one_message(self, mock_async_execute): fake_rows = [ (None, None, None, [ (("http://example.org/a", True), [1.0]), @@ -135,8 +141,8 @@ class TestGetGraphEmbeddings: store = _make_store() store.cassandra = Mock() - store.cassandra.execute = Mock(return_value=fake_rows) store.get_graph_embeddings_stmt = Mock() + mock_async_execute.return_value = fake_rows received = [] @@ -157,7 +163,8 @@ class TestGetTriples: the same Metadata construction. Cover it for parity.""" @pytest.mark.asyncio - async def test_row_converts_to_triples(self): + @patch('trustgraph.tables.knowledge.async_execute', new_callable=AsyncMock) + async def test_row_converts_to_triples(self, mock_async_execute): # row[3] is a list of (s_val, s_uri, p_val, p_uri, o_val, o_uri) fake_row = ( None, None, None, @@ -172,8 +179,8 @@ class TestGetTriples: store = _make_store() store.cassandra = Mock() - store.cassandra.execute = Mock(return_value=[fake_row]) store.get_triples_stmt = Mock() + mock_async_execute.return_value = [fake_row] received = []