Enhance bot functionality and refactor database interactions

- Added `ca-certificates` installation to Dockerfile for improved network security.
- Updated health check command in Dockerfile to include better timeout handling.
- Refactored `run_helper.py` to implement proper signal handling and logging during shutdown.
- Transitioned database operations to an asynchronous model in `async_db.py`, improving performance and responsiveness.
- Updated database schema to support new foreign key relationships and optimized indexing for better query performance.
- Enhanced various bot handlers to utilize async database methods, improving overall efficiency and user experience.
- Removed obsolete database and fix scripts to streamline the project structure.
This commit is contained in:
2025-09-02 18:22:02 +03:00
parent 013892dcb7
commit 1c6a37bc12
59 changed files with 5682 additions and 4204 deletions

View File

@@ -0,0 +1,208 @@
import pytest
import asyncio
import os
import tempfile
from datetime import datetime
from unittest.mock import Mock, AsyncMock
from database.repositories.post_repository import PostRepository
from database.models import TelegramPost, PostContent, MessageContentLink
@pytest.fixture(scope="session")
def event_loop():
"""Создает event loop для асинхронных тестов"""
loop = asyncio.get_event_loop_policy().new_event_loop()
yield loop
loop.close()
@pytest.fixture
def mock_post_repository():
"""Создает мок PostRepository для unit тестов"""
mock_repo = Mock(spec=PostRepository)
mock_repo._execute_query = AsyncMock()
mock_repo._execute_query_with_result = AsyncMock()
mock_repo.logger = Mock()
return mock_repo
@pytest.fixture
def sample_telegram_post():
"""Создает тестовый объект TelegramPost"""
return TelegramPost(
message_id=12345,
text="Тестовый пост для unit тестов",
author_id=67890,
helper_text_message_id=None,
created_at=int(datetime.now().timestamp())
)
@pytest.fixture
def sample_telegram_post_with_helper():
"""Создает тестовый объект TelegramPost с helper сообщением"""
return TelegramPost(
message_id=12346,
text="Тестовый пост с helper сообщением",
author_id=67890,
helper_text_message_id=99999,
created_at=int(datetime.now().timestamp())
)
@pytest.fixture
def sample_telegram_post_no_date():
"""Создает тестовый объект TelegramPost без даты"""
return TelegramPost(
message_id=12347,
text="Тестовый пост без даты",
author_id=67890,
helper_text_message_id=None,
created_at=None
)
@pytest.fixture
def sample_post_content():
"""Создает тестовый объект PostContent"""
return PostContent(
message_id=12345,
content_name="/path/to/test/file.jpg",
content_type="photo"
)
@pytest.fixture
def sample_message_content_link():
"""Создает тестовый объект MessageContentLink"""
return MessageContentLink(
post_id=12345,
message_id=67890
)
@pytest.fixture
def mock_db_execute_query():
"""Создает мок для _execute_query"""
return AsyncMock()
@pytest.fixture
def mock_db_execute_query_with_result():
"""Создает мок для _execute_query_with_result"""
return AsyncMock()
@pytest.fixture
def mock_logger():
"""Создает мок для logger"""
return Mock()
@pytest.fixture
def temp_db_file():
"""Создает временный файл БД для интеграционных тестов"""
with tempfile.NamedTemporaryFile(suffix='.db', delete=False) as tmp_file:
db_path = tmp_file.name
yield db_path
# Очищаем временный файл после тестов
try:
os.unlink(db_path)
except OSError:
pass
@pytest.fixture
def real_post_repository(temp_db_file):
"""Создает реальный PostRepository с временной БД для интеграционных тестов"""
return PostRepository(temp_db_file)
@pytest.fixture
def sample_posts_batch():
"""Создает набор тестовых постов для batch тестов"""
return [
TelegramPost(
message_id=10001,
text="Первый тестовый пост",
author_id=11111,
helper_text_message_id=None,
created_at=int(datetime.now().timestamp())
),
TelegramPost(
message_id=10002,
text="Второй тестовый пост",
author_id=22222,
helper_text_message_id=None,
created_at=int(datetime.now().timestamp())
),
TelegramPost(
message_id=10003,
text="Третий тестовый пост",
author_id=33333,
helper_text_message_id=88888,
created_at=int(datetime.now().timestamp())
)
]
@pytest.fixture
def sample_content_batch():
"""Создает набор тестового контента для batch тестов"""
return [
(10001, "/path/to/photo1.jpg", "photo"),
(10002, "/path/to/video1.mp4", "video"),
(10003, "/path/to/audio1.mp3", "audio"),
(10004, "/path/to/photo2.jpg", "photo"),
(10005, "/path/to/video2.mp4", "video")
]
@pytest.fixture
def mock_database_connection():
"""Создает мок для DatabaseConnection"""
mock_conn = Mock()
mock_conn._execute_query = AsyncMock()
mock_conn._execute_query_with_result = AsyncMock()
mock_conn.logger = Mock()
return mock_conn
@pytest.fixture
def sample_helper_message_ids():
"""Создает набор тестовых helper message ID"""
return [11111, 22222, 33333, 44444, 55555]
@pytest.fixture
def sample_message_ids():
"""Создает набор тестовых message ID"""
return [10001, 10002, 10003, 10004, 10005]
@pytest.fixture
def sample_author_ids():
"""Создает набор тестовых author ID"""
return [11111, 22222, 33333, 44444, 55555]
@pytest.fixture
def mock_sql_queries():
"""Создает мок для SQL запросов"""
return {
'create_tables': [
"CREATE TABLE IF NOT EXISTS post_from_telegram_suggest",
"CREATE TABLE IF NOT EXISTS content_post_from_telegram",
"CREATE TABLE IF NOT EXISTS message_link_to_content"
],
'add_post': "INSERT INTO post_from_telegram_suggest",
'update_helper': "UPDATE post_from_telegram_suggest SET helper_text_message_id",
'add_content': "INSERT OR IGNORE INTO content_post_from_telegram",
'add_link': "INSERT OR IGNORE INTO message_link_to_content",
'get_content': "SELECT cpft.content_name, cpft.content_type",
'get_text': "SELECT text FROM post_from_telegram_suggest",
'get_ids': "SELECT mltc.message_id",
'get_author': "SELECT author_id FROM post_from_telegram_suggest"
}