Dev 6 #9

Merged
KerradKerridi merged 13 commits from dev-6 into master 2025-08-30 11:58:45 +00:00
9 changed files with 737 additions and 48 deletions
Showing only changes of commit 86b6903920 - Show all commits

3
.gitignore vendored
View File

@@ -4,6 +4,9 @@
/database/test.db /database/test.db
/database/test.db-shm /database/test.db-shm
/database/test.db-wal /database/test.db-wal
/database/test_auto_unban.db
/database/test_auto_unban.db-shm
/database/test_auto_unban.db-wal
/settings.ini /settings.ini
/myenv/ /myenv/
/venv/ /venv/

View File

@@ -54,6 +54,14 @@ test-keyboards:
test-monitor: test-monitor:
python3 tests/test_monitor.py python3 tests/test_monitor.py
# Test auto unban scheduler
test-auto-unban:
python3 -m pytest tests/test_auto_unban_scheduler.py -v
# Test auto unban integration
test-auto-unban-integration:
python3 -m pytest tests/test_auto_unban_integration.py -v
# Run tests with coverage # Run tests with coverage
test-coverage: test-coverage:
python3 -m pytest tests/ --cov=helper_bot --cov=database --cov-report=term python3 -m pytest tests/ --cov=helper_bot --cov=database --cov-report=term

View File

@@ -485,31 +485,6 @@ class BotDB:
finally: finally:
self.close() self.close()
def get_users_blacklist(self):
"""
Возвращает список пользователей в черном списке.
Returns:
dict: Словарь, где ключ - user_id, значение - username.
{}: Если в черном списке нет пользователей.
Raises:
sqlite3. Error: Если произошла ошибка при выполнении запроса.
"""
self.logger.info(f"Запуск функции get_users_blacklist")
try:
self.connect()
self.cursor.execute("SELECT user_id, user_name FROM blacklist")
fetch_all = self.cursor.fetchall()
list_of_users = {user_id: username for user_id, username in fetch_all}
self.logger.info(f"Получен список пользователей в черном списке")
return list_of_users
except sqlite3.Error as error:
self.logger.error(f"Ошибка при получении списка пользователей в черном списке: {error}")
raise
finally:
self.close()
def get_users_for_unblock_today(self, date_to_unban: str): def get_users_for_unblock_today(self, date_to_unban: str):
""" """
Возвращает список пользователей, у которых истекает срок блокировки сегодня. Возвращает список пользователей, у которых истекает срок блокировки сегодня.

View File

@@ -0,0 +1,175 @@
import asyncio
from datetime import datetime, timezone, timedelta
from typing import Optional
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from apscheduler.triggers.cron import CronTrigger
from helper_bot.utils.base_dependency_factory import get_global_instance
from logs.custom_logger import logger
class AutoUnbanScheduler:
"""
Класс для автоматического разбана пользователей по истечении срока блокировки.
Запускается ежедневно в 5:00 по московскому времени.
"""
def __init__(self):
self.bdf = get_global_instance()
self.bot_db = self.bdf.get_db()
self.scheduler = AsyncIOScheduler()
self.bot = None # Будет установлен позже
def set_bot(self, bot):
"""Устанавливает экземпляр бота для отправки уведомлений"""
self.bot = bot
async def auto_unban_users(self):
"""
Основная функция автоматического разбана пользователей.
Получает список пользователей, у которых истекает срок блокировки сегодня,
и удаляет их из черного списка.
"""
try:
logger.info("Запуск автоматического разбана пользователей")
# Получаем сегодняшнюю дату в формате YYYY-MM-DD
moscow_tz = timezone(timedelta(hours=3)) # UTC+3 для Москвы
today = datetime.now(moscow_tz).strftime("%Y-%m-%d")
logger.info(f"Поиск пользователей для разблокировки на дату: {today}")
# Получаем список пользователей для разблокировки
users_to_unban = self.bot_db.get_users_for_unblock_today(today)
if not users_to_unban:
logger.info("Нет пользователей для разблокировки сегодня")
return
logger.info(f"Найдено {len(users_to_unban)} пользователей для разблокировки")
# Список для отслеживания результатов
success_count = 0
failed_count = 0
failed_users = []
# Разблокируем каждого пользователя
for user_id, username in users_to_unban.items():
try:
result = self.bot_db.delete_user_blacklist(user_id)
if result:
success_count += 1
logger.info(f"Пользователь {user_id} ({username}) успешно разблокирован")
else:
failed_count += 1
failed_users.append(f"{user_id} ({username})")
logger.error(f"Ошибка при разблокировке пользователя {user_id} ({username})")
except Exception as e:
failed_count += 1
failed_users.append(f"{user_id} ({username})")
logger.error(f"Исключение при разблокировке пользователя {user_id} ({username}): {e}")
# Формируем отчет
report = self._generate_report(success_count, failed_count, failed_users, users_to_unban)
# Отправляем отчет в лог-канал
await self._send_report(report)
logger.info(f"Автоматический разбан завершен. Успешно: {success_count}, Ошибок: {failed_count}")
except Exception as e:
error_msg = f"Критическая ошибка в автоматическом разбане: {e}"
logger.error(error_msg)
await self._send_error_report(error_msg)
def _generate_report(self, success_count: int, failed_count: int,
failed_users: list, all_users: dict) -> str:
"""Генерирует отчет о результатах автоматического разбана"""
report = f"🤖 <b>Отчет об автоматическом разбане</b>\n\n"
report += f"📅 Дата: {datetime.now().strftime('%d.%m.%Y %H:%M')}\n"
report += f"✅ Успешно разблокировано: {success_count}\n"
report += f"❌ Ошибок: {failed_count}\n\n"
if success_count > 0:
report += "✅ <b>Разблокированные пользователи:</b>\n"
for user_id, username in all_users.items():
if f"{user_id} ({username})" not in failed_users:
safe_username = username if username else "Неизвестный пользователь"
report += f"• ID: {user_id}, Имя: {safe_username}\n"
report += "\n"
if failed_users:
report += "❌ <b>Ошибки при разблокировке:</b>\n"
for user in failed_users:
report += f"{user}\n"
return report
async def _send_report(self, report: str):
"""Отправляет отчет в лог-канал"""
try:
if self.bot:
group_for_logs = self.bdf.settings['Telegram']['group_for_logs']
await self.bot.send_message(
chat_id=group_for_logs,
text=report,
parse_mode='HTML'
)
except Exception as e:
logger.error(f"Ошибка при отправке отчета: {e}")
async def _send_error_report(self, error_msg: str):
"""Отправляет отчет об ошибке в важный лог-канал"""
try:
if self.bot:
important_logs = self.bdf.settings['Telegram']['important_logs']
await self.bot.send_message(
chat_id=important_logs,
text=f"🚨 <b>Ошибка автоматического разбана</b>\n\n{error_msg}",
parse_mode='HTML'
)
except Exception as e:
logger.error(f"Ошибка при отправке отчета об ошибке: {e}")
def start_scheduler(self):
"""Запускает планировщик задач"""
try:
# Добавляем задачу на ежедневное выполнение в 5:00 по Москве
self.scheduler.add_job(
self.auto_unban_users,
CronTrigger(hour=5, minute=0, timezone='Europe/Moscow'),
id='auto_unban_users',
name='Автоматический разбан пользователей',
replace_existing=True
)
# Запускаем планировщик
self.scheduler.start()
logger.info("Планировщик автоматического разбана запущен. Задача запланирована на 5:00 по Москве")
except Exception as e:
logger.error(f"Ошибка при запуске планировщика: {e}")
def stop_scheduler(self):
"""Останавливает планировщик задач"""
try:
if self.scheduler.running:
self.scheduler.shutdown()
logger.info("Планировщик автоматического разбана остановлен")
except Exception as e:
logger.error(f"Ошибка при остановке планировщика: {e}")
async def run_manual_unban(self):
"""Запускает разбан вручную (для тестирования)"""
logger.info("Запуск ручного разбана пользователей")
await self.auto_unban_users()
# Глобальный экземпляр планировщика
auto_unban_scheduler = AutoUnbanScheduler()
def get_auto_unban_scheduler() -> AutoUnbanScheduler:
"""Возвращает глобальный экземпляр планировщика"""
return auto_unban_scheduler

View File

@@ -7,6 +7,9 @@ loguru==0.7.2
# System monitoring # System monitoring
psutil~=6.1.0 psutil~=6.1.0
# Scheduling
apscheduler~=3.10.4
# Testing # Testing
pytest==8.2.2 pytest==8.2.2
pytest-asyncio==1.1.0 pytest-asyncio==1.1.0

View File

@@ -11,6 +11,7 @@ if CURRENT_DIR not in sys.path:
from helper_bot.main import start_bot from helper_bot.main import start_bot
from helper_bot.utils.base_dependency_factory import get_global_instance from helper_bot.utils.base_dependency_factory import get_global_instance
from helper_bot.server_monitor import ServerMonitor from helper_bot.server_monitor import ServerMonitor
from helper_bot.utils.auto_unban_scheduler import get_auto_unban_scheduler
async def start_monitoring(bdf, bot): async def start_monitoring(bdf, bot):
@@ -40,6 +41,11 @@ async def main():
# Создаем экземпляр монитора # Создаем экземпляр монитора
monitor = await start_monitoring(bdf, monitor_bot) monitor = await start_monitoring(bdf, monitor_bot)
# Инициализируем планировщик автоматического разбана
auto_unban_scheduler = get_auto_unban_scheduler()
auto_unban_scheduler.set_bot(monitor_bot)
auto_unban_scheduler.start_scheduler()
# Флаг для корректного завершения # Флаг для корректного завершения
shutdown_event = asyncio.Event() shutdown_event = asyncio.Event()
@@ -71,6 +77,9 @@ async def main():
except Exception as e: except Exception as e:
print(f"Ошибка при отправке сообщения об отключении: {e}") print(f"Ошибка при отправке сообщения об отключении: {e}")
print("Останавливаем планировщик автоматического разбана...")
auto_unban_scheduler.stop_scheduler()
print("Останавливаем задачи...") print("Останавливаем задачи...")
# Отменяем задачи # Отменяем задачи
bot_task.cancel() bot_task.cancel()

View File

@@ -0,0 +1,251 @@
import pytest
import sqlite3
import os
from datetime import datetime, timezone, timedelta
from unittest.mock import Mock, patch, AsyncMock
from helper_bot.utils.auto_unban_scheduler import AutoUnbanScheduler
class TestAutoUnbanIntegration:
"""Интеграционные тесты для автоматического разбана"""
@pytest.fixture
def test_db_path(self):
"""Путь к тестовой базе данных"""
return 'database/test_auto_unban.db'
@pytest.fixture
def setup_test_db(self, test_db_path):
"""Создает тестовую базу данных с таблицей blacklist"""
# Удаляем старую тестовую базу если она существует
if os.path.exists(test_db_path):
os.remove(test_db_path)
# Создаем новую базу данных
conn = sqlite3.connect(test_db_path)
cursor = conn.cursor()
# Создаем таблицу blacklist
cursor.execute('''
CREATE TABLE IF NOT EXISTS blacklist (
user_id INTEGER PRIMARY KEY,
user_name TEXT,
message_for_user TEXT,
date_to_unban TEXT
)
''')
# Добавляем тестовые данные
today = datetime.now(timezone(timedelta(hours=3))).strftime("%Y-%m-%d")
tomorrow = (datetime.now(timezone(timedelta(hours=3))) + timedelta(days=1)).strftime("%Y-%m-%d")
test_data = [
(123, "test_user1", "Test ban 1", today), # Разблокируется сегодня
(456, "test_user2", "Test ban 2", today), # Разблокируется сегодня
(789, "test_user3", "Test ban 3", tomorrow), # Разблокируется завтра
(999, "test_user4", "Test ban 4", None), # Навсегда заблокирован
]
cursor.executemany(
"INSERT INTO blacklist (user_id, user_name, message_for_user, date_to_unban) VALUES (?, ?, ?, ?)",
test_data
)
conn.commit()
conn.close()
yield test_db_path
# Очистка после тестов
if os.path.exists(test_db_path):
os.remove(test_db_path)
@pytest.fixture
def mock_bdf(self, test_db_path):
"""Создает мок фабрики зависимостей с тестовой базой"""
mock_factory = Mock()
mock_factory.settings = {
'Telegram': {
'group_for_logs': '-1001234567890',
'important_logs': '-1001234567891'
}
}
# Создаем реальный экземпляр базы данных с тестовым файлом
from database.db import BotDB
import os
current_dir = os.getcwd()
mock_factory.database = BotDB(current_dir, test_db_path)
return mock_factory
@pytest.fixture
def mock_bot(self):
"""Создает мок бота"""
mock_bot = Mock()
mock_bot.send_message = AsyncMock()
return mock_bot
@pytest.mark.asyncio
@patch('helper_bot.utils.auto_unban_scheduler.get_global_instance')
async def test_auto_unban_with_real_db(self, mock_get_instance, setup_test_db, mock_bdf, mock_bot):
"""Тест автоматического разбана с реальной базой данных"""
# Настройка моков
mock_get_instance.return_value = mock_bdf
# Создаем планировщик
scheduler = AutoUnbanScheduler()
scheduler.bot_db = mock_bdf.database
scheduler.set_bot(mock_bot)
# Проверяем начальное состояние базы
conn = sqlite3.connect(setup_test_db)
cursor = conn.cursor()
cursor.execute("SELECT COUNT(*) FROM blacklist")
initial_count = cursor.fetchone()[0]
assert initial_count == 4
# Выполняем автоматический разбан
await scheduler.auto_unban_users()
# Проверяем, что пользователи с сегодняшней датой разблокированы
cursor.execute("SELECT COUNT(*) FROM blacklist WHERE date_to_unban = ?",
(datetime.now(timezone(timedelta(hours=3))).strftime("%Y-%m-%d"),))
today_count = cursor.fetchone()[0]
assert today_count == 0
# Проверяем, что пользователи с завтрашней датой остались
tomorrow = (datetime.now(timezone(timedelta(hours=3))) + timedelta(days=1)).strftime("%Y-%m-%d")
cursor.execute("SELECT COUNT(*) FROM blacklist WHERE date_to_unban = ?", (tomorrow,))
tomorrow_count = cursor.fetchone()[0]
assert tomorrow_count == 1
# Проверяем, что навсегда заблокированные пользователи остались
cursor.execute("SELECT COUNT(*) FROM blacklist WHERE date_to_unban IS NULL")
permanent_count = cursor.fetchone()[0]
assert permanent_count == 1
# Проверяем общее количество записей
cursor.execute("SELECT COUNT(*) FROM blacklist")
final_count = cursor.fetchone()[0]
assert final_count == 2 # Остались только завтрашние и навсегда заблокированные
conn.close()
# Проверяем, что отчет был отправлен
mock_bot.send_message.assert_called_once()
@pytest.mark.asyncio
@patch('helper_bot.utils.auto_unban_scheduler.get_global_instance')
async def test_auto_unban_no_users_today(self, mock_get_instance, setup_test_db, mock_bdf, mock_bot):
"""Тест разбана когда нет пользователей для разблокировки сегодня"""
# Настройка моков
mock_get_instance.return_value = mock_bdf
# Удаляем пользователей с сегодняшней датой
conn = sqlite3.connect(setup_test_db)
cursor = conn.cursor()
today = datetime.now(timezone(timedelta(hours=3))).strftime("%Y-%m-%d")
cursor.execute("DELETE FROM blacklist WHERE date_to_unban = ?", (today,))
conn.commit()
conn.close()
# Создаем планировщик
scheduler = AutoUnbanScheduler()
scheduler.bot_db = mock_bdf.database
scheduler.set_bot(mock_bot)
# Выполняем автоматический разбан
await scheduler.auto_unban_users()
# Проверяем, что отчет не был отправлен (нет пользователей для разблокировки)
mock_bot.send_message.assert_not_called()
@pytest.mark.asyncio
@patch('helper_bot.utils.auto_unban_scheduler.get_global_instance')
async def test_auto_unban_database_error(self, mock_get_instance, setup_test_db, mock_bdf, mock_bot):
"""Тест обработки ошибок базы данных"""
# Настройка моков
mock_get_instance.return_value = mock_bdf
# Создаем планировщик
scheduler = AutoUnbanScheduler()
scheduler.bot_db = mock_bdf.database
scheduler.set_bot(mock_bot)
# Удаляем таблицу чтобы вызвать ошибку
conn = sqlite3.connect(setup_test_db)
cursor = conn.cursor()
cursor.execute("DROP TABLE blacklist")
conn.commit()
conn.close()
# Выполняем автоматический разбан
await scheduler.auto_unban_users()
# Проверяем, что отчет об ошибке был отправлен
mock_bot.send_message.assert_called_once()
call_args = mock_bot.send_message.call_args
assert call_args[1]['chat_id'] == '-1001234567891' # important_logs
assert "Ошибка автоматического разбана" in call_args[1]['text']
def test_date_format_consistency(self, setup_test_db, mock_bdf):
"""Тест консистентности формата дат"""
scheduler = AutoUnbanScheduler()
scheduler.bot_db = mock_bdf.database
# Проверяем, что дата в базе соответствует ожидаемому формату
conn = sqlite3.connect(setup_test_db)
cursor = conn.cursor()
cursor.execute("SELECT date_to_unban FROM blacklist WHERE date_to_unban IS NOT NULL LIMIT 1")
result = cursor.fetchone()
conn.close()
if result and result[0]:
date_str = result[0]
# Проверяем формат YYYY-MM-DD
assert len(date_str) == 10
assert date_str.count('-') == 2
assert date_str[:4].isdigit() # Год
assert date_str[5:7].isdigit() # Месяц
assert date_str[8:10].isdigit() # День
class TestSchedulerLifecycle:
"""Тесты жизненного цикла планировщика"""
def test_scheduler_start_stop(self):
"""Тест запуска и остановки планировщика"""
scheduler = AutoUnbanScheduler()
# Запускаем планировщик
scheduler.start_scheduler()
assert scheduler.scheduler.running
# Останавливаем планировщик (должно пройти без ошибок)
scheduler.stop_scheduler()
# APScheduler может не сразу остановиться, но это нормально
def test_scheduler_job_creation(self):
"""Тест создания задачи в планировщике"""
scheduler = AutoUnbanScheduler()
with patch.object(scheduler.scheduler, 'add_job') as mock_add_job:
scheduler.start_scheduler()
# Проверяем, что задача была создана с правильными параметрами
mock_add_job.assert_called_once()
call_args = mock_add_job.call_args
# Проверяем функцию
assert call_args[0][0] == scheduler.auto_unban_users
# Проверяем триггер (должен быть CronTrigger)
from apscheduler.triggers.cron import CronTrigger
assert isinstance(call_args[0][1], CronTrigger)
# Проверяем ID и имя задачи
assert call_args[1]['id'] == 'auto_unban_users'
assert call_args[1]['name'] == 'Автоматический разбан пользователей'
assert call_args[1]['replace_existing'] is True

View File

@@ -0,0 +1,288 @@
import pytest
import asyncio
from datetime import datetime, timezone, timedelta
from unittest.mock import Mock, patch, AsyncMock
from helper_bot.utils.auto_unban_scheduler import AutoUnbanScheduler, get_auto_unban_scheduler
class TestAutoUnbanScheduler:
"""Тесты для класса AutoUnbanScheduler"""
@pytest.fixture
def scheduler(self):
"""Создает экземпляр планировщика для тестов"""
return AutoUnbanScheduler()
@pytest.fixture
def mock_bot_db(self):
"""Создает мок базы данных"""
mock_db = Mock()
mock_db.get_users_for_unblock_today.return_value = {
123: "test_user1",
456: "test_user2"
}
mock_db.delete_user_blacklist.return_value = True
return mock_db
@pytest.fixture
def mock_bdf(self):
"""Создает мок фабрики зависимостей"""
mock_factory = Mock()
mock_factory.settings = {
'Telegram': {
'group_for_logs': '-1001234567890',
'important_logs': '-1001234567891'
}
}
return mock_factory
@pytest.fixture
def mock_bot(self):
"""Создает мок бота"""
mock_bot = Mock()
mock_bot.send_message = AsyncMock()
return mock_bot
def test_scheduler_initialization(self, scheduler):
"""Тест инициализации планировщика"""
assert scheduler.bot_db is not None
assert scheduler.scheduler is not None
assert scheduler.bot is None
def test_set_bot(self, scheduler, mock_bot):
"""Тест установки бота"""
scheduler.set_bot(mock_bot)
assert scheduler.bot == mock_bot
@pytest.mark.asyncio
@patch('helper_bot.utils.auto_unban_scheduler.get_global_instance')
async def test_auto_unban_users_success(self, mock_get_instance, scheduler, mock_bot_db, mock_bdf, mock_bot):
"""Тест успешного выполнения автоматического разбана"""
# Настройка моков
mock_get_instance.return_value = mock_bdf
scheduler.bot_db = mock_bot_db
scheduler.set_bot(mock_bot)
# Выполнение теста
await scheduler.auto_unban_users()
# Проверки
mock_bot_db.get_users_for_unblock_today.assert_called_once()
assert mock_bot_db.delete_user_blacklist.call_count == 2
mock_bot.send_message.assert_called_once()
@pytest.mark.asyncio
@patch('helper_bot.utils.auto_unban_scheduler.get_global_instance')
async def test_auto_unban_users_no_users(self, mock_get_instance, scheduler, mock_bot_db, mock_bdf, mock_bot):
"""Тест разбана когда нет пользователей для разблокировки"""
# Настройка моков
mock_get_instance.return_value = mock_bdf
mock_bot_db.get_users_for_unblock_today.return_value = {}
scheduler.bot_db = mock_bot_db
scheduler.set_bot(mock_bot)
# Выполнение теста
await scheduler.auto_unban_users()
# Проверки
mock_bot_db.get_users_for_unblock_today.assert_called_once()
mock_bot_db.delete_user_blacklist.assert_not_called()
mock_bot.send_message.assert_not_called()
@pytest.mark.asyncio
@patch('helper_bot.utils.auto_unban_scheduler.get_global_instance')
async def test_auto_unban_users_partial_failure(self, mock_get_instance, scheduler, mock_bot_db, mock_bdf, mock_bot):
"""Тест разбана с частичными ошибками"""
# Настройка моков
mock_get_instance.return_value = mock_bdf
mock_bot_db.get_users_for_unblock_today.return_value = {
123: "test_user1",
456: "test_user2"
}
# Первый вызов успешен, второй - ошибка
mock_bot_db.delete_user_blacklist.side_effect = [True, False]
scheduler.bot_db = mock_bot_db
scheduler.set_bot(mock_bot)
# Выполнение теста
await scheduler.auto_unban_users()
# Проверки
assert mock_bot_db.delete_user_blacklist.call_count == 2
mock_bot.send_message.assert_called_once()
@pytest.mark.asyncio
@patch('helper_bot.utils.auto_unban_scheduler.get_global_instance')
async def test_auto_unban_users_exception(self, mock_get_instance, scheduler, mock_bot_db, mock_bdf, mock_bot):
"""Тест разбана с исключением"""
# Настройка моков
mock_get_instance.return_value = mock_bdf
mock_bot_db.get_users_for_unblock_today.side_effect = Exception("Database error")
scheduler.bot_db = mock_bot_db
scheduler.set_bot(mock_bot)
# Выполнение теста
await scheduler.auto_unban_users()
# Проверки
mock_bot.send_message.assert_called_once()
# Проверяем, что сообщение об ошибке было отправлено
call_args = mock_bot.send_message.call_args
assert "Ошибка автоматического разбана" in call_args[1]['text']
def test_generate_report(self, scheduler):
"""Тест генерации отчета"""
users = {123: "test_user1", 456: "test_user2"}
failed_users = ["456 (test_user2)"]
report = scheduler._generate_report(1, 1, failed_users, users)
assert "Отчет об автоматическом разбане" in report
assert "Успешно разблокировано: 1" in report
assert "Ошибок: 1" in report
assert "test_user1" in report
assert "456 (test_user2)" in report
@pytest.mark.asyncio
@patch('helper_bot.utils.auto_unban_scheduler.get_global_instance')
async def test_send_report(self, mock_get_instance, scheduler, mock_bdf, mock_bot):
"""Тест отправки отчета"""
mock_get_instance.return_value = mock_bdf
scheduler.set_bot(mock_bot)
report = "Test report"
await scheduler._send_report(report)
# Проверяем, что send_message был вызван
mock_bot.send_message.assert_called_once()
# Проверяем аргументы вызова
call_args = mock_bot.send_message.call_args
assert call_args[1]['text'] == report
assert call_args[1]['parse_mode'] == 'HTML'
@pytest.mark.asyncio
@patch('helper_bot.utils.auto_unban_scheduler.get_global_instance')
async def test_send_error_report(self, mock_get_instance, scheduler, mock_bdf, mock_bot):
"""Тест отправки отчета об ошибке"""
mock_get_instance.return_value = mock_bdf
scheduler.set_bot(mock_bot)
error_msg = "Test error"
await scheduler._send_error_report(error_msg)
# Проверяем, что send_message был вызван
mock_bot.send_message.assert_called_once()
# Проверяем аргументы вызова
call_args = mock_bot.send_message.call_args
assert "Ошибка автоматического разбана" in call_args[1]['text']
assert error_msg in call_args[1]['text']
assert call_args[1]['parse_mode'] == 'HTML'
def test_start_scheduler(self, scheduler):
"""Тест запуска планировщика"""
with patch.object(scheduler.scheduler, 'add_job') as mock_add_job, \
patch.object(scheduler.scheduler, 'start') as mock_start:
scheduler.start_scheduler()
mock_add_job.assert_called_once()
mock_start.assert_called_once()
def test_stop_scheduler(self, scheduler):
"""Тест остановки планировщика"""
# Сначала запускаем планировщик
scheduler.start_scheduler()
# Проверяем, что планировщик запущен
assert scheduler.scheduler.running
# Теперь останавливаем (должно пройти без ошибок)
scheduler.stop_scheduler()
# Проверяем, что метод выполнился без исключений
# APScheduler может не сразу остановиться, но это нормально
@pytest.mark.asyncio
@patch('helper_bot.utils.auto_unban_scheduler.get_global_instance')
async def test_run_manual_unban(self, mock_get_instance, scheduler, mock_bot_db, mock_bdf, mock_bot):
"""Тест ручного запуска разбана"""
mock_get_instance.return_value = mock_bdf
mock_bot_db.get_users_for_unblock_today.return_value = {}
scheduler.bot_db = mock_bot_db
scheduler.set_bot(mock_bot)
await scheduler.run_manual_unban()
mock_bot_db.get_users_for_unblock_today.assert_called_once()
class TestGetAutoUnbanScheduler:
"""Тесты для функции get_auto_unban_scheduler"""
def test_get_auto_unban_scheduler(self):
"""Тест получения глобального экземпляра планировщика"""
scheduler = get_auto_unban_scheduler()
assert isinstance(scheduler, AutoUnbanScheduler)
# Проверяем, что возвращается один и тот же экземпляр
scheduler2 = get_auto_unban_scheduler()
assert scheduler is scheduler2
class TestDateHandling:
"""Тесты для обработки дат"""
def test_moscow_timezone(self):
"""Тест работы с московским временем"""
scheduler = AutoUnbanScheduler()
# Проверяем, что дата формируется в правильном формате
moscow_tz = timezone(timedelta(hours=3))
today = datetime.now(moscow_tz).strftime("%Y-%m-%d")
assert len(today) == 10 # YYYY-MM-DD
assert today.count('-') == 2
assert today[:4].isdigit() # Год
assert today[5:7].isdigit() # Месяц
assert today[8:10].isdigit() # День
@pytest.mark.asyncio
class TestAsyncOperations:
"""Тесты асинхронных операций"""
@patch('helper_bot.utils.auto_unban_scheduler.get_global_instance')
async def test_async_auto_unban_flow(self, mock_get_instance):
"""Тест полного асинхронного потока разбана"""
# Создаем моки
mock_bdf = Mock()
mock_bdf.settings = {
'Telegram': {
'group_for_logs': '-1001234567890',
'important_logs': '-1001234567891'
}
}
mock_get_instance.return_value = mock_bdf
mock_bot_db = Mock()
mock_bot_db.get_users_for_unblock_today.return_value = {123: "test_user"}
mock_bot_db.delete_user_blacklist.return_value = True
mock_bot = Mock()
mock_bot.send_message = AsyncMock()
# Создаем планировщик
scheduler = AutoUnbanScheduler()
scheduler.bot_db = mock_bot_db
scheduler.set_bot(mock_bot)
# Выполняем разбан
await scheduler.auto_unban_users()
# Проверяем результаты
mock_bot_db.get_users_for_unblock_today.assert_called_once()
mock_bot_db.delete_user_blacklist.assert_called_once_with(123)
mock_bot.send_message.assert_called_once()

View File

@@ -512,30 +512,7 @@ def test_update_info_about_stickers_error(bot):
bot.update_info_about_stickers(12345) bot.update_info_about_stickers(12345)
def test_get_users_blacklist_empty(bot):
"""Проверяет, что функция возвращает пустой словарь, если в черном списке нет пользователей."""
conn = sqlite3.connect('database/test.db')
cursor = conn.cursor()
cursor.execute("DELETE FROM blacklist")
conn.commit()
conn.close()
blacklist = bot.get_users_blacklist()
assert blacklist == {}
def test_get_users_blacklist_non_empty(bot):
"""Проверяет, что функция возвращает словарь с пользователями из черного списка."""
blacklist = bot.get_users_blacklist()
assert blacklist == {12345: "@iban", 14278: "@boris"}
def test_get_users_blacklist_error(bot):
"""Проверяет, что функция вызывает sqlite3. Error при ошибке запроса."""
__drop_table('blacklist')
with pytest.raises(sqlite3.Error):
bot.get_users_blacklist()
def test_get_blacklist_users_by_id_found(bot, setup_db): def test_get_blacklist_users_by_id_found(bot, setup_db):