"""Unit tests for persistence manager methods. Tests cover: - execute_async() with mock database - get_db_engine() with mock database manager """ from __future__ import annotations import pytest from unittest.mock import Mock, AsyncMock, MagicMock from importlib import import_module import sqlalchemy def get_persistence_module(): """Lazy import to avoid circular import issues.""" return import_module('langbot.pkg.persistence.mgr') class TestExecuteAsync: """Tests for execute_async method.""" @pytest.mark.asyncio async def test_execute_async_calls_engine_execute(self): """Test that execute_async calls engine execute.""" persistence = get_persistence_module() mock_app = Mock() mock_app.persistence_mgr = None mgr = persistence.PersistenceManager(mock_app) # Mock database manager with async engine mock_engine = MagicMock() mock_conn = AsyncMock() mock_conn.execute = AsyncMock(return_value=Mock()) mock_conn.commit = AsyncMock() # Setup the async context manager async_cm = AsyncMock() async_cm.__aenter__ = AsyncMock(return_value=mock_conn) async_cm.__aexit__ = AsyncMock(return_value=None) mock_engine.connect = Mock(return_value=async_cm) mock_db = Mock() mock_db.get_engine = Mock(return_value=mock_engine) mgr.db = mock_db # Execute a simple select await mgr.execute_async(sqlalchemy.select(1)) mock_conn.execute.assert_called_once() mock_conn.commit.assert_called_once() @pytest.mark.asyncio async def test_execute_async_returns_result(self): """Test that execute_async returns the result from execute. NOTE: This test verifies the return value chain - that the result from conn.execute() is properly returned by execute_async(). The mock verifies the value propagation, not the SQL execution. For real SQL execution tests, see integration tests. """ persistence = get_persistence_module() mock_app = Mock() mgr = persistence.PersistenceManager(mock_app) # Create a mock result with actual attributes to simulate real result mock_result = Mock(name='query_result') mock_result.scalar = Mock(return_value=1) # Simulate scalar() method mock_result.scalars = Mock() # Simulate scalars() method mock_engine = MagicMock() mock_conn = AsyncMock() mock_conn.execute = AsyncMock(return_value=mock_result) mock_conn.commit = AsyncMock() async_cm = AsyncMock() async_cm.__aenter__ = AsyncMock(return_value=mock_conn) async_cm.__aexit__ = AsyncMock(return_value=None) mock_engine.connect = Mock(return_value=async_cm) mock_db = Mock() mock_db.get_engine = Mock(return_value=mock_engine) mgr.db = mock_db result = await mgr.execute_async(sqlalchemy.text("SELECT 1")) # Verify result is the same object returned by execute assert result is mock_result # Verify result has expected methods (simulating real Result object) assert hasattr(result, 'scalar') assert result.scalar() == 1 class TestGetDbEngine: """Tests for get_db_engine method.""" def test_get_db_engine_returns_engine_from_db_manager(self): """Test that get_db_engine returns engine from db manager.""" persistence = get_persistence_module() mock_app = Mock() mgr = persistence.PersistenceManager(mock_app) mock_engine = Mock(name='engine') mock_db = Mock() mock_db.get_engine = Mock(return_value=mock_engine) mgr.db = mock_db engine = mgr.get_db_engine() assert engine == mock_engine mock_db.get_engine.assert_called_once() def test_get_db_engine_without_db_set_raises(self): """Test that get_db_engine raises when db is not set.""" persistence = get_persistence_module() mock_app = Mock() mgr = persistence.PersistenceManager(mock_app) # db is not initialized mgr.db = None with pytest.raises(AttributeError): mgr.get_db_engine() class TestSerializeModelEdgeCases: """Tests for serialize_model edge cases.""" def test_serialize_model_with_all_columns_masked(self): """Test serialize_model when all columns are masked.""" persistence = get_persistence_module() from sqlalchemy import Column, Integer, String from sqlalchemy.orm import declarative_base Base = declarative_base() class SimpleModel(Base): __tablename__ = 'simple' id = Column(Integer, primary_key=True) name = Column(String(50)) mock_app = Mock() mgr = persistence.PersistenceManager(mock_app) instance = SimpleModel(id=1, name='test') result = mgr.serialize_model(SimpleModel, instance, masked_columns=['id', 'name']) # Result should be empty dict when all columns masked assert result == {}