import asyncio import os import sys from pathlib import Path from unittest.mock import AsyncMock, Mock, patch # Корень проекта (каталог с helper_bot и database) — в sys.path для импортов _conftest_dir = Path(__file__).resolve().parent _project_root = _conftest_dir.parent if str(_project_root) not in sys.path: sys.path.insert(0, str(_project_root)) import pytest from aiogram.fsm.context import FSMContext from aiogram.types import Chat, Message, User from database.async_db import AsyncBotDB # Импортируем моки в самом начале import tests.mocks # Настройка pytest-asyncio pytest_plugins = ("pytest_asyncio",) @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_message(): """Создает базовый мок сообщения для тестов""" message = Mock(spec=Message) message.from_user = Mock(spec=User) message.from_user.id = 123456 message.from_user.full_name = "Test User" message.from_user.username = "testuser" message.from_user.first_name = "Test" message.from_user.is_bot = False message.from_user.language_code = "ru" message.chat = Mock(spec=Chat) message.chat.id = 123456 message.chat.type = "private" message.message_id = 1 message.text = "/start" message.forward = AsyncMock() message.answer = AsyncMock() message.answer_sticker = AsyncMock() message.bot.send_message = AsyncMock() return message @pytest.fixture def mock_state(): """Создает мок состояния FSM для тестов""" state = Mock(spec=FSMContext) state.set_state = AsyncMock() state.get_state = AsyncMock(return_value="START") return state @pytest.fixture def mock_db(): """Создает мок базы данных для тестов""" db = Mock(spec=AsyncBotDB) db.user_exists = Mock(return_value=False) db.add_new_user = Mock() db.update_user_date = Mock() db.update_user_info = Mock() db.add_post_in_db = Mock() db.update_stickers_info = Mock() db.add_new_message_in_db = Mock() db.get_stickers_info = Mock(return_value=False) db.get_username_and_full_name = Mock(return_value=("testuser", "Test User")) return db @pytest.fixture def mock_bot(): """Создает мок бота для тестов""" bot = AsyncMock() bot.send_message = AsyncMock() bot.delete_webhook = AsyncMock() return bot @pytest.fixture def mock_dispatcher(): """Создает мок диспетчера для тестов""" dispatcher = AsyncMock() dispatcher.include_routers = Mock() dispatcher.start_polling = AsyncMock() return dispatcher @pytest.fixture def test_settings(): """Возвращает тестовые настройки""" return { "Telegram": { "bot_token": "test_token_123", "preview_link": False, "group_for_posts": "-1001234567890", "group_for_message": "-1001234567891", "main_public": "-1001234567892", "group_for_logs": "-1001234567893", "important_logs": "-1001234567894", }, "Settings": {"logs": True, "test": False}, } @pytest.fixture def mock_factory(test_settings, mock_db): """Создает мок фабрики зависимостей""" factory = Mock() factory.settings = test_settings factory.get_db = Mock(return_value=mock_db) factory.database = mock_db return factory @pytest.fixture def sample_photo_message(mock_message): """Создает сообщение с фото для тестов""" mock_message.content_type = "photo" mock_message.caption = "Тестовое фото" mock_message.media_group_id = None mock_message.photo = [Mock()] mock_message.photo[-1].file_id = "photo_file_id" return mock_message @pytest.fixture def sample_video_message(mock_message): """Создает сообщение с видео для тестов""" mock_message.content_type = "video" mock_message.caption = "Тестовое видео" mock_message.media_group_id = None mock_message.video = Mock() mock_message.video.file_id = "video_file_id" return mock_message @pytest.fixture def sample_audio_message(mock_message): """Создает сообщение с аудио для тестов""" mock_message.content_type = "audio" mock_message.caption = "Тестовое аудио" mock_message.media_group_id = None mock_message.audio = Mock() mock_message.audio.file_id = "audio_file_id" return mock_message @pytest.fixture def sample_voice_message(mock_message): """Создает голосовое сообщение для тестов""" mock_message.content_type = "voice" mock_message.media_group_id = None mock_message.voice = Mock() mock_message.voice.file_id = "voice_file_id" return mock_message @pytest.fixture def sample_video_note_message(mock_message): """Создает видеокружок для тестов""" mock_message.content_type = "video_note" mock_message.media_group_id = None mock_message.video_note = Mock() mock_message.video_note.file_id = "video_note_file_id" return mock_message @pytest.fixture def sample_media_group(mock_message): """Создает медиагруппу для тестов""" mock_message.media_group_id = "group_123" mock_message.content_type = "photo" album = [mock_message] album[0].caption = "Подпись к медиагруппе" return album @pytest.fixture def sample_text_message(mock_message): """Создает текстовое сообщение для тестов""" mock_message.content_type = "text" mock_message.text = "Тестовое текстовое сообщение" mock_message.media_group_id = None return mock_message @pytest.fixture def sample_document_message(mock_message): """Создает сообщение с документом для тестов""" mock_message.content_type = "document" mock_message.media_group_id = None return mock_message # Маркеры для категоризации тестов def pytest_configure(config): """Настройка маркеров pytest""" config.addinivalue_line("markers", "asyncio: mark test as async") config.addinivalue_line("markers", "slow: mark test as slow") config.addinivalue_line("markers", "integration: mark test as integration test") config.addinivalue_line("markers", "unit: mark test as unit test") # Автоматическая маркировка тестов def pytest_collection_modifyitems(config, items): """Автоматически маркирует тесты по их расположению""" for item in items: # Маркируем асинхронные тесты if "async" in item.name or "Async" in item.name: item.add_marker(pytest.mark.asyncio) # Маркируем интеграционные тесты if "integration" in item.name.lower() or "Integration" in str(item.cls): item.add_marker(pytest.mark.integration) # Маркируем unit тесты if "unit" in item.name.lower() or "Unit" in str(item.cls): item.add_marker(pytest.mark.unit) # Маркируем медленные тесты if "slow" in item.name.lower() or "Slow" in str(item.cls): item.add_marker(pytest.mark.slow)