"""Тесты для scheduler_main.py — standalone scraper-контейнер (#1182). (a) модуль импортируется без реального DB/сети. (b) _should_run() возвращает False при SCHEDULER_ENABLE=false. (c) _run() отменяется чисто без unhandled exceptions. """ from __future__ import annotations import asyncio import os os.environ.setdefault("DATABASE_URL", "postgresql+psycopg://test:test@localhost:5432/test") import pytest import app.scheduler_main as sm # (a) Импорт модуля def test_scheduler_main_importable() -> None: """scheduler_main импортируется без ошибок.""" assert sm is not None assert hasattr(sm, "_should_run") assert hasattr(sm, "_run") # (b) Kill-switch SCHEDULER_ENABLE=false def test_should_run_false_when_disabled(monkeypatch: pytest.MonkeyPatch) -> None: """_should_run() == False когда SCHEDULER_ENABLE отключён.""" monkeypatch.setattr(sm.settings, "scheduler_enable", False) assert sm._should_run() is False def test_should_run_true_when_enabled(monkeypatch: pytest.MonkeyPatch) -> None: """_should_run() == True при включённом scheduler.""" monkeypatch.setattr(sm.settings, "scheduler_enable", True) assert sm._should_run() is True # (c) _run() отменяется чисто @pytest.mark.asyncio async def test_run_cancels_cleanly(monkeypatch: pytest.MonkeyPatch) -> None: """_run() завершается без unhandled exceptions при отмене задачи.""" cancelled = asyncio.Event() async def _stub_loop() -> None: """Короткая заглушка: ждёт отмены, имитируя бесконечный scheduler_loop.""" cancelled.set() await asyncio.sleep(999) monkeypatch.setattr("app.scheduler_main.scheduler_loop", _stub_loop, raising=False) # Патчим импорт внутри _run (он делает `from app.services.scheduler import scheduler_loop`) import app.services.scheduler as _sched_mod monkeypatch.setattr(_sched_mod, "scheduler_loop", _stub_loop) # Также патчим внутри самого модуля если он уже закешировал ссылку # (на случай если _run делает локальный import каждый раз — оба патча идемпотентны) task = asyncio.create_task(sm._run()) # Ждём пока _stub_loop запустится (флаг) await asyncio.wait_for(cancelled.wait(), timeout=2.0) task.cancel() # Не должно бросить никакого исключения try: await task except asyncio.CancelledError: pass # CancelledError от самого task — допустимо # Тест прошёл если не было unhandled exception assert task.done()