""" Extended Tests for Database Manager Phase 21: Database & Encryption - Database Manager Tests Tests encrypted database management, connection pooling, and thread safety. """ import pytest from unittest.mock import patch, MagicMock from pathlib import Path class TestDatabaseEncryption: """Tests for database encryption functionality""" @patch("local_deep_research.database.encrypted_db.get_sqlcipher_module") @patch("local_deep_research.database.encrypted_db.get_data_directory") def test_encryption_key_validation_valid( self, mock_data_dir, mock_sqlcipher ): """Test valid encryption key is accepted""" from local_deep_research.database.encrypted_db import DatabaseManager mock_data_dir.return_value = Path("/tmp/test_data") with patch.object( DatabaseManager, "_check_encryption_available", return_value=True ): manager = DatabaseManager() assert manager._is_valid_encryption_key("valid_password") is True assert manager._is_valid_encryption_key("a") is True assert manager._is_valid_encryption_key("complex!@#$%") is True @patch("local_deep_research.database.encrypted_db.get_sqlcipher_module") @patch("local_deep_research.database.encrypted_db.get_data_directory") def test_encryption_key_validation_invalid( self, mock_data_dir, mock_sqlcipher ): """Test invalid encryption keys are rejected""" from local_deep_research.database.encrypted_db import DatabaseManager mock_data_dir.return_value = Path("/tmp/test_data") with patch.object( DatabaseManager, "_check_encryption_available", return_value=True ): manager = DatabaseManager() assert manager._is_valid_encryption_key(None) is False assert manager._is_valid_encryption_key("") is False @patch("local_deep_research.database.encrypted_db.get_sqlcipher_module") @patch("local_deep_research.database.encrypted_db.get_data_directory") def test_database_creation_invalid_password( self, mock_data_dir, mock_sqlcipher ): """Test database creation fails with invalid password""" from local_deep_research.database.encrypted_db import DatabaseManager mock_data_dir.return_value = Path("/tmp/test_data") with patch.object( DatabaseManager, "_check_encryption_available", return_value=True ): manager = DatabaseManager() with pytest.raises(ValueError, match="Invalid encryption key"): manager.create_user_database("testuser", "") with pytest.raises(ValueError, match="Invalid encryption key"): manager.create_user_database("testuser", None) @patch("local_deep_research.database.encrypted_db.get_sqlcipher_module") @patch("local_deep_research.database.encrypted_db.get_data_directory") def test_database_open_invalid_password( self, mock_data_dir, mock_sqlcipher ): """Test opening database fails with invalid password""" from local_deep_research.database.encrypted_db import DatabaseManager mock_data_dir.return_value = Path("/tmp/test_data") with patch.object( DatabaseManager, "_check_encryption_available", return_value=True ): manager = DatabaseManager() with pytest.raises(ValueError, match="Invalid encryption key"): manager.open_user_database("testuser", "") @patch("local_deep_research.database.encrypted_db.get_data_directory") def test_sqlcipher_unavailable_fallback(self, mock_data_dir): """Test fallback when SQLCipher not available""" from local_deep_research.database.encrypted_db import DatabaseManager mock_data_dir.return_value = Path("/tmp/test_data") with patch.dict("os.environ", {"LDR_ALLOW_UNENCRYPTED": "true"}): with patch.object( DatabaseManager, "_check_encryption_available", return_value=False, ): manager = DatabaseManager() assert manager.has_encryption is False @patch("local_deep_research.database.encrypted_db.get_data_directory") def test_encryption_check_available(self, mock_data_dir): """Test encryption availability check""" from local_deep_research.database.encrypted_db import DatabaseManager mock_data_dir.return_value = Path("/tmp/test_data") # With encryption available with patch.object( DatabaseManager, "_check_encryption_available", return_value=True ): manager = DatabaseManager() assert manager.has_encryption is True class TestConnectionPooling: """Tests for connection pooling functionality""" @patch("local_deep_research.database.encrypted_db.get_data_directory") def test_pool_kwargs_static_pool(self, mock_data_dir): """Test pool kwargs for static pool (testing mode)""" from local_deep_research.database.encrypted_db import DatabaseManager mock_data_dir.return_value = Path("/tmp/test_data") with patch.dict("os.environ", {"TESTING": "true"}): with patch.object( DatabaseManager, "_check_encryption_available", return_value=True, ): manager = DatabaseManager() kwargs = manager._get_pool_kwargs() assert kwargs == {} @patch("local_deep_research.database.encrypted_db.get_data_directory") def test_pool_kwargs_queue_pool(self, mock_data_dir): """Test pool kwargs for queue pool (production mode)""" from local_deep_research.database.encrypted_db import DatabaseManager mock_data_dir.return_value = Path("/tmp/test_data") with patch.dict("os.environ", {}, clear=True): with patch.object( DatabaseManager, "_check_encryption_available", return_value=True, ): manager = DatabaseManager() manager._use_static_pool = False kwargs = manager._get_pool_kwargs() assert "pool_size" in kwargs assert kwargs["pool_size"] == 20 assert kwargs["max_overflow"] == 40 assert kwargs["pool_timeout"] == 10 @patch("local_deep_research.database.encrypted_db.get_data_directory") def test_connection_storage(self, mock_data_dir): """Test connections are stored properly""" from local_deep_research.database.encrypted_db import DatabaseManager mock_data_dir.return_value = Path("/tmp/test_data") with patch.object( DatabaseManager, "_check_encryption_available", return_value=True ): manager = DatabaseManager() # Mock engine mock_engine = MagicMock() manager.connections["testuser"] = mock_engine assert "testuser" in manager.connections assert manager.connections["testuser"] is mock_engine class TestThreadSafety: """Tests for thread safety functionality""" class TestDatabaseOperations: """Tests for database operations""" @patch("local_deep_research.database.encrypted_db.get_data_directory") def test_get_session_no_connection(self, mock_data_dir): """Test get_session when no connection exists""" from local_deep_research.database.encrypted_db import DatabaseManager mock_data_dir.return_value = Path("/tmp/test_data") with patch.object( DatabaseManager, "_check_encryption_available", return_value=True ): manager = DatabaseManager() result = manager.get_session("nonexistent_user") assert result is None @patch("local_deep_research.database.encrypted_db.get_data_directory") def test_get_session_with_connection(self, mock_data_dir): """Test get_session when connection exists""" from local_deep_research.database.encrypted_db import DatabaseManager mock_data_dir.return_value = Path("/tmp/test_data") with patch.object( DatabaseManager, "_check_encryption_available", return_value=True ): manager = DatabaseManager() mock_engine = MagicMock() manager.connections["testuser"] = mock_engine # This will try to create a real session, mock the sessionmaker with patch( "local_deep_research.database.encrypted_db.sessionmaker" ) as mock_sm: mock_session = MagicMock() mock_sm.return_value = MagicMock(return_value=mock_session) result = manager.get_session("testuser") assert result is mock_session @patch("local_deep_research.database.encrypted_db.get_data_directory") def test_close_user_database(self, mock_data_dir): """Test closing user database""" from local_deep_research.database.encrypted_db import DatabaseManager mock_data_dir.return_value = Path("/tmp/test_data") with patch.object( DatabaseManager, "_check_encryption_available", return_value=True ): manager = DatabaseManager() mock_engine = MagicMock() manager.connections["testuser"] = mock_engine manager.close_user_database("testuser") mock_engine.dispose.assert_called_once() assert "testuser" not in manager.connections @patch("local_deep_research.database.encrypted_db.get_data_directory") def test_get_memory_usage(self, mock_data_dir): """Test memory usage statistics""" from local_deep_research.database.encrypted_db import DatabaseManager mock_data_dir.return_value = Path("/tmp/test_data") with patch.object( DatabaseManager, "_check_encryption_available", return_value=True ): manager = DatabaseManager() manager.connections["user1"] = MagicMock() manager.connections["user2"] = MagicMock() usage = manager.get_memory_usage() assert usage["active_connections"] == 2 assert "thread_engines" not in usage assert "estimated_memory_mb" in usage class TestDatabaseIntegrity: """Tests for database integrity checking""" @patch("local_deep_research.database.encrypted_db.get_data_directory") def test_check_integrity_no_connection(self, mock_data_dir): """Test integrity check when no connection exists""" from local_deep_research.database.encrypted_db import DatabaseManager mock_data_dir.return_value = Path("/tmp/test_data") with patch.object( DatabaseManager, "_check_encryption_available", return_value=True ): manager = DatabaseManager() result = manager.check_database_integrity("nonexistent") assert result is False @patch("local_deep_research.database.encrypted_db.get_data_directory") def test_check_integrity_success(self, mock_data_dir): """Test successful integrity check""" from local_deep_research.database.encrypted_db import DatabaseManager mock_data_dir.return_value = Path("/tmp/test_data") with patch.object( DatabaseManager, "_check_encryption_available", return_value=True ): manager = DatabaseManager() mock_engine = MagicMock() mock_conn = MagicMock() mock_engine.connect.return_value.__enter__ = MagicMock( return_value=mock_conn ) mock_engine.connect.return_value.__exit__ = MagicMock( return_value=False ) # Mock successful integrity checks mock_conn.execute.side_effect = [ MagicMock( fetchone=MagicMock(return_value=("ok",)) ), # quick_check iter([]), # cipher_integrity_check - no failures ] manager.connections["testuser"] = mock_engine result = manager.check_database_integrity("testuser") assert result is True @patch("local_deep_research.database.encrypted_db.get_data_directory") def test_check_integrity_failure(self, mock_data_dir): """Test failed integrity check""" from local_deep_research.database.encrypted_db import DatabaseManager mock_data_dir.return_value = Path("/tmp/test_data") with patch.object( DatabaseManager, "_check_encryption_available", return_value=True ): manager = DatabaseManager() mock_engine = MagicMock() mock_conn = MagicMock() mock_engine.connect.return_value.__enter__ = MagicMock( return_value=mock_conn ) mock_engine.connect.return_value.__exit__ = MagicMock( return_value=False ) # Mock failed integrity check mock_conn.execute.return_value.fetchone.return_value = ("corrupt",) manager.connections["testuser"] = mock_engine result = manager.check_database_integrity("testuser") assert result is False class TestPasswordChange: """Tests for password change functionality""" @patch("local_deep_research.database.encrypted_db.get_data_directory") def test_change_password_no_encryption(self, mock_data_dir): """Test password change when encryption not available""" from local_deep_research.database.encrypted_db import DatabaseManager mock_data_dir.return_value = Path("/tmp/test_data") with patch.object( DatabaseManager, "_check_encryption_available", return_value=False ): with patch.dict("os.environ", {"LDR_ALLOW_UNENCRYPTED": "true"}): manager = DatabaseManager() result = manager.change_password("user", "old", "new") assert result is False class TestUserExists: """Tests for user existence check""" @patch("local_deep_research.database.encrypted_db.get_data_directory") def test_user_exists_true(self, mock_data_dir): """Test user exists returns true""" from local_deep_research.database.encrypted_db import DatabaseManager mock_data_dir.return_value = Path("/tmp/test_data") with patch.object( DatabaseManager, "_check_encryption_available", return_value=True ): manager = DatabaseManager() # Mock the internal method call with patch.object( manager, "user_exists", return_value=True ) as mock_method: result = mock_method("testuser") assert result is True @patch("local_deep_research.database.encrypted_db.get_data_directory") def test_user_exists_false(self, mock_data_dir): """Test user exists returns false""" from local_deep_research.database.encrypted_db import DatabaseManager mock_data_dir.return_value = Path("/tmp/test_data") with patch.object( DatabaseManager, "_check_encryption_available", return_value=True ): manager = DatabaseManager() with patch.object( manager, "user_exists", return_value=False ) as mock_method: result = mock_method("nonexistent") assert result is False class TestUserDatabasePath: """Tests for database path generation""" def test_get_user_db_path(self): """Test user database path generation""" from local_deep_research.database.encrypted_db import DatabaseManager # Use a temp directory that exists import tempfile temp_dir = Path(tempfile.gettempdir()) with patch( "local_deep_research.database.encrypted_db.get_data_directory", return_value=temp_dir, ): with patch.object( DatabaseManager, "_check_encryption_available", return_value=True, ): with patch( "local_deep_research.database.encrypted_db.get_user_database_filename", return_value="user_test.db", ): manager = DatabaseManager() path = manager._get_user_db_path("testuser") # Path should include the filename assert "user_test.db" in str(path) class TestGlobalInstance: """Tests for global database manager instance""" @pytest.mark.skip(reason="documentation/placeholder test - not implemented") def test_global_instance_exists(self): """Test global db_manager instance is available""" # This will fail if the module can't be imported # but we mock the initialization pass # Just a placeholder - actual import tested elsewhere