Files
bottohelp/test_integration/test_warnings_integration.py

407 lines
20 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Интеграционный тест для проверки работы системы предупреждений.
Тестирует весь путь: добавление предупреждения → отображение в /info команде.
Проверяет поддержку русского языка, эмодзи и UTF-8 кодировки.
"""
import pytest
import tempfile
import os
from unittest.mock import Mock, AsyncMock
from datetime import datetime
# Добавляем корневую директорию в путь
import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from core.application import Application
from core.config import Config
class TestWarningsIntegration:
"""Интеграционные тесты системы предупреждений с поддержкой UTF-8"""
@pytest.fixture
def temp_config(self):
"""Временный файл конфигурации для интеграционных тестов"""
with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False, encoding='utf-8') as f:
f.write("""
BOT_TOKEN = "123456789:integration_test_token"
ADMIN_IDS = [123456789, 987654321]
OPENWEATHER_API_KEY = "test_key"
NEWS_API_KEY = "test_key"
OPENAI_API_KEY = "test_key"
""")
config_path = f.name
yield config_path
# Очистка
if os.path.exists(config_path):
os.unlink(config_path)
@pytest.fixture
def temp_db(self):
"""Временная база данных для тестов с инициализацией таблиц"""
from database.models import DatabaseSchema
from database.repository import UserRepository, ScoreRepository
with tempfile.NamedTemporaryFile(suffix='.db', delete=False) as f:
db_path = f.name
# Инициализируем базу данных
repo = UserRepository(db_path)
try:
# Создаем все таблицы
for table_sql in DatabaseSchema.get_create_tables_sql():
repo._execute_query(table_sql)
# Создаем стандартные достижения
repo.initialize_achievements()
finally:
repo.close()
yield db_path
# Очистка
if os.path.exists(db_path):
os.unlink(db_path)
@pytest.fixture
def mock_update(self):
"""Мокированный объект Update для команд"""
update = Mock()
# Мокаем пользователя
user = Mock()
user.id = 123456789
user.username = "test_user"
user.first_name = "Тестовый"
user.last_name = "Пользователь"
update.effective_user = user
# Мокаем сообщение
message = Mock()
message.message_id = 1
message.reply_text = AsyncMock()
update.message = message
# Мокаем чат
chat = Mock()
chat.id = -1001234567890
update.effective_chat = chat
return update
@pytest.fixture
def mock_context(self):
"""Мокированный контекст бота"""
context = Mock()
context.args = []
# Мокаем приложение
app = Mock()
app._date_time = datetime.now()
context._application = app
# Мокаем бота
bot = AsyncMock()
context.bot = bot
context.user_data = {}
return context
@pytest.mark.asyncio
async def test_full_warnings_flow_utf8(self, temp_config, temp_db, mock_update, mock_context):
"""Интеграционный тест полного цикла предупреждений с поддержкой UTF-8"""
# Создаем конфигурацию
config = Config(temp_config)
# Создаем репозитории и сервисы
from database.repository import UserRepository, ScoreRepository
user_repo = UserRepository(temp_db)
score_repo = ScoreRepository(temp_db)
from services.user_service import UserService
user_service = UserService(user_repo, score_repo)
from services.moderation_service import ModerationService
moderation_service = ModerationService(user_repo, score_repo)
# Создаем обработчик команд
from handlers.user_handlers import UserHandlers
user_handlers = UserHandlers(config, config, user_service)
try:
# Шаг 1: Создаем пользователя
user_profile = await user_service.get_or_create_user(
mock_update.effective_user.id,
mock_update.effective_user.username,
mock_update.effective_user.first_name,
mock_update.effective_user.last_name
)
assert user_profile is not None, "Пользователь должен быть создан"
assert user_profile.warnings == 0, "Изначально предупреждений должно быть 0"
# Шаг 2: Добавляем предупреждение с UTF-8 текстом
test_reasons = [
"Спам в чате 🚫",
"Нецензурная лексика с эмодзи 😡",
"Нарушение правил поведения 📋",
"Русский текст с UTF-8 символами: Привет мир! 🌍",
"Специальные символы: àáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ",
"Кириллица: АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя"
]
admin_id = 987654321 # ID администратора
for i, reason in enumerate(test_reasons):
print(f"[TEST] Добавляем предупреждение {i+1}: '{reason}'")
result = await moderation_service.warn_user(
mock_update.effective_user.id,
reason,
admin_id
)
assert result['success'] == True, f"Предупреждение {i+1} должно быть добавлено успешно"
assert result['warnings_count'] == i + 1, f"Количество предупреждений должно быть {i+1}"
assert result['reason'] == reason, f"Причина предупреждения должна сохраниться: {reason}"
# Проверяем, что предупреждение сохранено в базе данных
current_warnings = user_repo.get_warnings_count(mock_update.effective_user.id)
assert current_warnings == i + 1, f"В базе данных должно быть {i+1} предупреждение(й)"
# Шаг 3: Проверяем отображение в команде /info
await user_handlers.handle_info(mock_update, mock_context)
# Проверяем, что ответ был отправлен
mock_update.message.reply_text.assert_called_once()
# Получаем отправленный текст
call_args = mock_update.message.reply_text.call_args
response_text = call_args[0][0] # Первый позиционный аргумент
# Проверяем содержимое ответа
assert "👤" in response_text, "Ответ должен содержать иконку пользователя"
assert "Информация о пользователе" in response_text, "Ответ должен содержать заголовок"
assert "Тестовый" in response_text, "Должно отображаться имя пользователя"
assert f"⚠️ Предупреждений: {len(test_reasons)}" in response_text, f"Должно отображаться {len(test_reasons)} предупреждений"
# Проверяем, что все UTF-8 символы сохранены корректно
for reason in test_reasons:
# Проверяем, что ключевые части причин присутствуют в ответе
if "Спам в чате" in reason:
assert "Спам" in response_text, "Текст с кириллицей должен отображаться корректно"
if "🚫" in reason:
assert "🚫" in response_text, "Эмодзи должны отображаться корректно"
if "🌍" in reason:
assert "🌍" in response_text, "Эмодзи должны отображаться корректно"
print("[SUCCESS] Все предупреждения корректно сохранены и отображаются в /info")
finally:
# Очищаем ресурсы
user_repo.close()
score_repo.close()
@pytest.mark.asyncio
async def test_warnings_with_special_characters(self, temp_config, temp_db):
"""Тест предупреждений с специальными символами UTF-8"""
from database.repository import UserRepository, ScoreRepository
from services.user_service import UserService
from services.moderation_service import ModerationService
user_repo = UserRepository(temp_db)
score_repo = ScoreRepository(temp_db)
user_service = UserService(user_repo, score_repo)
moderation_service = ModerationService(user_repo, score_repo)
try:
test_user_id = 111111111
admin_id = 222222222
# Создаем пользователя
await user_service.get_or_create_user(test_user_id, "testuser", "Тест", "Юзер")
# Тестируем различные специальные символы
special_reasons = [
"Математические символы: ∑∏√∫∆∞≠≈≤≥",
"Стрелки: ←↑→↓↔⇄⇅",
"Фигурки: ●○■□◆◇★☆♡♢♤♧",
"Валюты: ¢£¤¥€₽₿",
"Дроби: ½⅓¼¾⅕⅙⅛⅔⅖⅚⅜⅗⅘⅙⅚⅛⅜⅝⅞⅟",
"Надстрочные: ⁰¹²³⁴⁵⁶⁷⁸⁹⁺⁻⁼⁽⁾ⁿ",
"Подстрочные: ₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎",
"Акценты: àáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ",
"Греческий: αβγδεζηθικλμνξοπρστυφχψω",
"Китайские иероглифы: 你好世界こんにちは안녕하세요"
]
for reason in special_reasons:
print(f"[TEST] Тестируем специальную строку: '{reason}'")
result = await moderation_service.warn_user(test_user_id, reason, admin_id)
assert result['success'] == True, f"Предупреждение с символами '{reason[:20]}...' должно быть добавлено"
assert result['reason'] == reason, f"Текст предупреждения должен сохраниться без изменений: {reason}"
# Проверяем в базе данных
warnings_count = user_repo.get_warnings_count(test_user_id)
assert warnings_count > 0, "Предупреждение должно быть сохранено в БД"
print("[SUCCESS] Все специальные символы UTF-8 корректно обработаны")
finally:
user_repo.close()
score_repo.close()
@pytest.mark.asyncio
async def test_warnings_display_in_info_command(self, temp_config, temp_db, mock_update, mock_context):
"""Тест отображения предупреждений в команде /info"""
from database.repository import UserRepository, ScoreRepository
from services.user_service import UserService
from services.moderation_service import ModerationService
from handlers.user_handlers import UserHandlers
from core.config import Config
config = Config(temp_config)
user_repo = UserRepository(temp_db)
score_repo = ScoreRepository(temp_db)
user_service = UserService(user_repo, score_repo)
moderation_service = ModerationService(user_repo, score_repo)
user_handlers = UserHandlers(config, config, user_service)
try:
# Создаем пользователя
await user_service.get_or_create_user(
mock_update.effective_user.id,
mock_update.effective_user.username,
mock_update.effective_user.first_name,
mock_update.effective_user.last_name
)
# Добавляем несколько предупреждений с эмодзи
warnings_to_add = [
"Первое предупреждение ⚠️",
"Второе нарушение 🚫",
"Третье замечание 📋"
]
for reason in warnings_to_add:
await moderation_service.warn_user(
mock_update.effective_user.id,
reason,
987654321
)
# Выполняем команду /info
await user_handlers.handle_info(mock_update, mock_context)
# Получаем ответ
call_args = mock_update.message.reply_text.call_args
response_text = call_args[0][0]
# Проверяем отображение количества предупреждений
assert "⚠️ Предупреждений: 3" in response_text, "Должно отображаться правильное количество предупреждений"
# Проверяем, что имя пользователя отображается корректно
assert "Тестовый" in response_text, "Имя пользователя должно отображаться"
print("[SUCCESS] Команда /info корректно отображает предупреждения")
finally:
user_repo.close()
score_repo.close()
@pytest.mark.asyncio
async def test_warnings_persistence_across_sessions(self, temp_config, temp_db):
"""Тест сохранения предупреждений между сессиями"""
from database.repository import UserRepository, ScoreRepository
from services.user_service import UserService
from services.moderation_service import ModerationService
# Первая "сессия" - добавляем предупреждения
user_repo1 = UserRepository(temp_db)
score_repo1 = ScoreRepository(temp_db)
user_service1 = UserService(user_repo1, score_repo1)
moderation_service1 = ModerationService(user_repo1, score_repo1)
try:
test_user_id = 333333333
admin_id = 444444444
# Создаем пользователя и добавляем предупреждения
await user_service1.get_or_create_user(test_user_id, "persist_test", "Персист", "Тест")
test_reason = "Тест persistence с эмодзи 💾"
await moderation_service1.warn_user(test_user_id, test_reason, admin_id)
initial_count = user_repo1.get_warnings_count(test_user_id)
assert initial_count == 1, "Предупреждение должно быть сохранено"
finally:
user_repo1.close()
score_repo1.close()
# Вторая "сессия" - проверяем, что предупреждения сохранились
user_repo2 = UserRepository(temp_db)
score_repo2 = ScoreRepository(temp_db)
user_service2 = UserService(user_repo2, score_repo2)
try:
# Получаем профиль пользователя
user_profile = await user_service2.get_or_create_user(test_user_id, "persist_test", "Персист", "Тест")
# Проверяем, что предупреждения сохранились
final_count = user_repo2.get_warnings_count(test_user_id)
assert final_count == 1, "Предупреждение должно сохраниться между сессиями"
assert user_profile.warnings == 1, "Количество предупреждений в профиле должно быть корректным"
print("[SUCCESS] Предупреждения корректно сохраняются между сессиями")
finally:
user_repo2.close()
score_repo2.close()
@pytest.mark.asyncio
async def test_multiple_users_warnings_isolation(self, temp_config, temp_db):
"""Тест изоляции предупреждений между пользователями"""
from database.repository import UserRepository, ScoreRepository
from services.user_service import UserService
from services.moderation_service import ModerationService
user_repo = UserRepository(temp_db)
score_repo = ScoreRepository(temp_db)
user_service = UserService(user_repo, score_repo)
moderation_service = ModerationService(user_repo, score_repo)
try:
# Создаем двух пользователей
user1_id = 555555555
user2_id = 666666666
admin_id = 777777777
await user_service.get_or_create_user(user1_id, "user1", "Пользователь", "Первый")
await user_service.get_or_create_user(user2_id, "user2", "Пользователь", "Второй")
# Добавляем предупреждения первому пользователю
await moderation_service.warn_user(user1_id, "Предупреждение для первого пользователя 👤", admin_id)
await moderation_service.warn_user(user1_id, "Второе предупреждение для первого 🚨", admin_id)
# Добавляем одно предупреждение второму пользователю
await moderation_service.warn_user(user2_id, "Предупреждение для второго пользователя 🎯", admin_id)
# Проверяем изоляцию
user1_warnings = user_repo.get_warnings_count(user1_id)
user2_warnings = user_repo.get_warnings_count(user2_id)
assert user1_warnings == 2, "Первый пользователь должен иметь 2 предупреждения"
assert user2_warnings == 1, "Второй пользователь должен иметь 1 предупреждение"
print("[SUCCESS] Предупреждения пользователей изолированы друг от друга")
finally:
user_repo.close()
score_repo.close()