Scheduler.__init__ больше не создаёт task синхронно; start() стал async-методом

This commit is contained in:
deadzilla 2026-07-14 22:20:45 +05:00
parent 66b16e95f4
commit bb9f291623
3 changed files with 16 additions and 13 deletions

1
bot.py
View File

@ -143,6 +143,7 @@ class BotRunner:
morning_time = os.getenv("MORNING_TIME", "07:00") morning_time = os.getenv("MORNING_TIME", "07:00")
self.scheduler = Scheduler(self.bot, morning_time) self.scheduler = Scheduler(self.bot, morning_time)
self.bot._scheduler = self.scheduler self.bot._scheduler = self.scheduler
await self.scheduler.start()
logger.info( logger.info(
"Планировщик запущен (время: %s, сервер: %s)", morning_time, guild.name "Планировщик запущен (время: %s, сервер: %s)", morning_time, guild.name
) )

View File

@ -25,12 +25,12 @@ class TestSchedulerInit:
scheduler = Scheduler(bot) scheduler = Scheduler(bot)
assert scheduler.morning_time == "07:00" assert scheduler.morning_time == "07:00"
def test_init_creates_task(self) -> None: def test_init_does_not_start_scheduler(self) -> None:
"""Инициализация должна вызывать _start_scheduler.""" """Инициализация не должна запускать планировщик (start() вызывается отдельно)."""
bot = AsyncMock() bot = AsyncMock()
with patch.object(Scheduler, "_start_scheduler") as mock_start: with patch.object(Scheduler, "_start_scheduler") as mock_start:
Scheduler(bot) Scheduler(bot)
mock_start.assert_called_once() mock_start.assert_not_called()
class TestSchedulerCalculateNextRun: class TestSchedulerCalculateNextRun:
@ -67,14 +67,14 @@ class TestSchedulerCalculateNextRun:
class TestSchedulerStartStop: class TestSchedulerStartStop:
"""Тесты запуска/остановки планировщика.""" """Тесты запуска/остановки планировщика."""
def test_start_starts_task(self) -> None: @pytest.mark.asyncio
"""start() должен вызывать _start_scheduler (1 в __init__ + 1 в start, но реальный task один).""" async def test_start_starts_task(self) -> None:
"""start() должен вызывать _start_scheduler один раз."""
bot = AsyncMock() bot = AsyncMock()
with patch.object(Scheduler, "_start_scheduler") as mock_start: with patch.object(Scheduler, "_start_scheduler") as mock_start:
scheduler = Scheduler(bot) scheduler = Scheduler(bot)
scheduler.start() await scheduler.start()
# __init__ вызывает _start_scheduler, start() тоже вызывает mock_start.assert_called_once()
assert mock_start.call_count == 2
def test_stop_stops_task(self) -> None: def test_stop_stops_task(self) -> None:
"""stop() должен остановить task.""" """stop() должен остановить task."""
@ -88,12 +88,15 @@ class TestSchedulerStartStop:
scheduler.stop() scheduler.stop()
assert scheduler._running is False assert scheduler._running is False
def test_double_start_no_duplicate(self) -> None: @pytest.mark.asyncio
"""Повторный start должен вызывать _start_scheduler дважды (реальный task не дублируется благодаря флагам).""" async def test_double_start_no_duplicate(self) -> None:
"""Повторный start() не должен дублировать task (благодаря флагам)."""
bot = AsyncMock() bot = AsyncMock()
with patch.object(Scheduler, "_start_scheduler") as mock_start: with patch.object(Scheduler, "_start_scheduler") as mock_start:
scheduler = Scheduler(bot) scheduler = Scheduler(bot)
scheduler.start() # второй вызов await scheduler.start()
await scheduler.start() # повторный вызов
# _start_scheduler вызывается 2 раза, но реальный task один (флаг _running защищает)
assert mock_start.call_count == 2 assert mock_start.call_count == 2

View File

@ -151,7 +151,6 @@ class Scheduler:
) )
self._task: asyncio.Task | None = None self._task: asyncio.Task | None = None
self._running = False self._running = False
self._start_scheduler()
def _start_scheduler(self): def _start_scheduler(self):
if self._running: if self._running:
@ -276,7 +275,7 @@ class Scheduler:
if not sent: if not sent:
logger.error("Не удалось найти канал для отправки morning-дайджеста") logger.error("Не удалось найти канал для отправки morning-дайджеста")
def start(self) -> None: async def start(self) -> None:
self._start_scheduler() self._start_scheduler()
def stop(self) -> None: def stop(self) -> None: