"""Тесты для utils/logger.py — проверка настройки логирования.""" import logging import os import tempfile from pathlib import Path from utils.logger import setup_logging def test_console_handler_added() -> None: """setup_logging добавляет StreamHandler на stdout.""" with _isolated_logger() as root: handlers = [h for h in root.handlers if isinstance(h, logging.StreamHandler)] assert len(handlers) >= 1 def test_default_level_is_info() -> None: """Уровень по умолчанию — INFO.""" with _isolated_logger() as root: assert root.level == logging.INFO def test_custom_level_from_env() -> None: """LOG_LEVEL из окружения влияет на уровень.""" os.environ["LOG_LEVEL"] = "DEBUG" try: with _isolated_logger() as root: assert root.level == logging.DEBUG finally: os.environ.pop("LOG_LEVEL", None) def test_invalid_level_defaults_to_info() -> None: """Неизвестный LOG_LEVEL fallback на INFO.""" os.environ["LOG_LEVEL"] = "NOTAREALLEVEL" try: with _isolated_logger() as root: assert root.level == logging.INFO finally: os.environ.pop("LOG_LEVEL", None) def test_file_handler_when_logs_dir_exists() -> None: """RotatingFileHandler добавляется если директория logs существует.""" import logging.handlers orig_cwd = os.getcwd() with tempfile.TemporaryDirectory() as tmpdir: logs_dir = Path(tmpdir) / "logs" logs_dir.mkdir() os.chdir(logs_dir.parent) try: with _isolated_logger() as root: file_handlers = [ h for h in root.handlers if isinstance(h, logging.handlers.RotatingFileHandler) ] assert len(file_handlers) >= 1 # Закрыть file handler чтобы освободить файл на Windows for h in file_handlers: h.close() finally: os.chdir(orig_cwd) def test_logs_dir_created_automatically() -> None: """Директория logs создаётся автоматически, если её нет.""" import logging.handlers orig_cwd = os.getcwd() with tempfile.TemporaryDirectory() as tmpdir: os.chdir(tmpdir) # Убедиться что logs/ нет logs_dir = Path("logs") assert not logs_dir.exists() try: with _isolated_logger() as root: assert logs_dir.exists() file_handlers = [ h for h in root.handlers if isinstance(h, logging.handlers.RotatingFileHandler) ] assert len(file_handlers) >= 1 # Закрыть file handler чтобы освободить файл на Windows for h in file_handlers: h.close() finally: os.chdir(orig_cwd) def test_aiohttp_suppressed() -> None: """Логгер aiohttp подавлен до WARNING.""" with _isolated_logger(): aiohttp_logger = logging.getLogger("aiohttp") assert aiohttp_logger.level == logging.WARNING def test_discord_level_is_info() -> None: """Логгер discord установлен на INFO.""" with _isolated_logger(): discord_logger = logging.getLogger("discord") assert discord_logger.level == logging.INFO def test_log_message_format() -> None: """Формат сообщения: время, уровень, имя модуля, текст.""" import io import sys with _isolated_logger() as root: # Replace stdout with our buffer buffer = io.StringIO() for handler in root.handlers: if isinstance(handler, logging.StreamHandler): handler.stream = buffer test_logger = logging.getLogger("test_module") test_logger.info("test message") output = buffer.getvalue() assert "INFO" in output assert "test_module" in output assert "test message" in output import contextlib @contextlib.contextmanager def _isolated_logger(): """Создать изолированный root-логгер без handlers из других тестов.""" old_handlers = logging.getLogger().handlers[:] old_level = logging.getLogger().level logging.getLogger().handlers.clear() yield setup_logging() # Restore logging.getLogger().handlers.clear() logging.getLogger().handlers.extend(old_handlers) logging.getLogger().setLevel(old_level)