Исправление: вынес import из тел функций тестов наверх модулей (35 вхождений)
This commit is contained in:
parent
beae42fdc8
commit
a8d2bdb579
@ -11,6 +11,8 @@ import sys
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import discord
|
||||
|
||||
# Добавляем корень проекта в путь импорта
|
||||
ROOT_DIR = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(ROOT_DIR))
|
||||
@ -38,7 +40,6 @@ class TestBotErrorHandling:
|
||||
def test_bot_handles_login_failure(self) -> None:
|
||||
"""BotRunner.run() обрабатывает discord.LoginFailure."""
|
||||
import bot
|
||||
import discord
|
||||
|
||||
runner = bot.BotRunner()
|
||||
with patch.object(runner.bot, "start", side_effect=discord.LoginFailure("bad token")):
|
||||
@ -51,7 +52,6 @@ class TestBotErrorHandling:
|
||||
def test_bot_handles_http_exception(self) -> None:
|
||||
"""BotRunner.run() обрабатывает discord.HTTPException."""
|
||||
import bot
|
||||
import discord
|
||||
|
||||
runner = bot.BotRunner()
|
||||
mock_response = MagicMock(status=502)
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
import pytest
|
||||
from unittest.mock import AsyncMock, MagicMock
|
||||
|
||||
from commands.stats import Stats
|
||||
|
||||
class TestStatsCommand:
|
||||
"""Тесты Discord-команды stats."""
|
||||
@ -15,8 +16,6 @@ class TestStatsCommand:
|
||||
|
||||
async def test_stats_sends_embed(self) -> None:
|
||||
"""Команда stats отправляет embed-сообщение."""
|
||||
from commands.stats import Stats
|
||||
|
||||
mock_ctx = MagicMock()
|
||||
mock_ctx.bot.latency = 0.035
|
||||
mock_ctx.bot.guilds = []
|
||||
@ -32,8 +31,6 @@ class TestStatsCommand:
|
||||
|
||||
async def test_stats_correct_values(self) -> None:
|
||||
"""Значения серверов, каналов и пользователей считаются верно."""
|
||||
from commands.stats import Stats
|
||||
|
||||
guild1 = self._make_mock_guild(channel_count=10, member_count=200)
|
||||
guild2 = self._make_mock_guild(channel_count=5, member_count=50)
|
||||
|
||||
@ -56,8 +53,6 @@ class TestStatsCommand:
|
||||
|
||||
async def test_stats_empty_guilds(self) -> None:
|
||||
"""Пустой список серверов не вызывает ошибок."""
|
||||
from commands.stats import Stats
|
||||
|
||||
mock_ctx = MagicMock()
|
||||
mock_ctx.bot.latency = 0.050
|
||||
mock_ctx.bot.guilds = []
|
||||
@ -75,8 +70,6 @@ class TestStatsCommand:
|
||||
|
||||
async def test_stats_none_member_count(self) -> None:
|
||||
"""member_count=None не вызывает ошибок."""
|
||||
from commands.stats import Stats
|
||||
|
||||
guild = self._make_mock_guild(channel_count=3, member_count=None)
|
||||
|
||||
mock_ctx = MagicMock()
|
||||
@ -95,7 +88,6 @@ class TestStatsCommand:
|
||||
async def test_stats_excludes_categories(self) -> None:
|
||||
"""Категории не входят в счётчик каналов."""
|
||||
import discord
|
||||
from commands.stats import Stats
|
||||
|
||||
guild = MagicMock()
|
||||
text_ch = MagicMock()
|
||||
|
||||
@ -4,14 +4,14 @@ import time
|
||||
import pytest
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
from commands.status import Status
|
||||
|
||||
|
||||
class TestStatusCommand:
|
||||
"""Тесты Discord-команды status."""
|
||||
|
||||
async def test_status_sends_embed(self) -> None:
|
||||
"""Команда status отправляет embed-сообщение."""
|
||||
from commands.status import Status
|
||||
|
||||
mock_ctx = MagicMock()
|
||||
mock_ctx.bot.latency = 0.042
|
||||
mock_ctx.bot._start_time = time.time()
|
||||
@ -28,8 +28,6 @@ class TestStatusCommand:
|
||||
|
||||
async def test_status_uptime_format(self) -> None:
|
||||
"""Uptime форматируется корректно."""
|
||||
from commands.status import Status
|
||||
|
||||
mock_ctx = MagicMock()
|
||||
mock_ctx.bot.latency = 0.050
|
||||
mock_ctx.bot._start_time = time.time() - 90061 # 1д 1ч 1м 1с
|
||||
@ -51,29 +49,21 @@ class TestFormatUptime:
|
||||
"""Тесты форматирования uptime."""
|
||||
|
||||
def test_zero_seconds(self) -> None:
|
||||
from commands.status import Status
|
||||
|
||||
result = Status._format_uptime(0)
|
||||
assert result == "0с"
|
||||
|
||||
def test_minutes_and_seconds(self) -> None:
|
||||
from commands.status import Status
|
||||
|
||||
result = Status._format_uptime(125) # 2м 5с
|
||||
assert "2м" in result
|
||||
assert "5с" in result
|
||||
|
||||
def test_hours_minutes_seconds(self) -> None:
|
||||
from commands.status import Status
|
||||
|
||||
result = Status._format_uptime(3661) # 1ч 1м 1с
|
||||
assert "1ч" in result
|
||||
assert "1м" in result
|
||||
assert "1с" in result
|
||||
|
||||
def test_full_day(self) -> None:
|
||||
from commands.status import Status
|
||||
|
||||
result = Status._format_uptime(90061) # 1д 1ч 1м 1с
|
||||
assert "1д" in result
|
||||
assert "1ч" in result
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import json
|
||||
import pytest
|
||||
import requests
|
||||
from requests.exceptions import ConnectionError, Timeout, SSLError
|
||||
from unittest.mock import patch, MagicMock
|
||||
from utils.cat import fetch_cat
|
||||
|
||||
@ -30,7 +32,6 @@ class TestFetchCat:
|
||||
@patch("utils.cat._session.get")
|
||||
async def test_fetch_cat_http_error(self, mock_get) -> None:
|
||||
"""HTTP-ошибка (raise_for_status) должна вернуть None."""
|
||||
import requests
|
||||
mock_response = MagicMock()
|
||||
mock_response.raise_for_status.side_effect = requests.HTTPError("404 Not Found")
|
||||
mock_get.return_value = mock_response
|
||||
@ -40,7 +41,6 @@ class TestFetchCat:
|
||||
@patch("utils.cat._session.get")
|
||||
async def test_fetch_cat_connection_error(self, mock_get) -> None:
|
||||
"""ConnectionError должна вернуть None."""
|
||||
from requests.exceptions import ConnectionError
|
||||
mock_get.side_effect = ConnectionError("No connection")
|
||||
result = await fetch_cat()
|
||||
assert result is None
|
||||
@ -48,7 +48,6 @@ class TestFetchCat:
|
||||
@patch("utils.cat._session.get")
|
||||
async def test_fetch_cat_timeout(self, mock_get) -> None:
|
||||
"""Timeout должна вернуть None."""
|
||||
from requests.exceptions import Timeout
|
||||
mock_get.side_effect = Timeout("Request timed out")
|
||||
result = await fetch_cat()
|
||||
assert result is None
|
||||
@ -56,7 +55,6 @@ class TestFetchCat:
|
||||
@patch("utils.cat._session.get")
|
||||
async def test_fetch_cat_ssl_error(self, mock_get) -> None:
|
||||
"""SSLError должна вернуть None."""
|
||||
from requests.exceptions import SSLError
|
||||
mock_get.side_effect = SSLError("SSL handshake failed")
|
||||
result = await fetch_cat()
|
||||
assert result is None
|
||||
@ -64,7 +62,6 @@ class TestFetchCat:
|
||||
@patch("utils.cat._session.get")
|
||||
async def test_fetch_cat_json_parse_error(self, mock_get) -> None:
|
||||
"""Ошибка парсинга JSON должна вернуть None."""
|
||||
import requests
|
||||
mock_response = MagicMock()
|
||||
mock_response.json.side_effect = requests.JSONDecodeError("Expecting value", "", 0)
|
||||
mock_response.raise_for_status = MagicMock()
|
||||
@ -85,7 +82,6 @@ class TestFetchCat:
|
||||
@patch("utils.cat._session.get")
|
||||
async def test_fetch_cat_request_exception(self, mock_get) -> None:
|
||||
"""Общий RequestException должен вернуть None."""
|
||||
import requests
|
||||
mock_get.side_effect = requests.RequestException("Generic error")
|
||||
result = await fetch_cat()
|
||||
assert result is None
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
|
||||
import pytest
|
||||
import requests
|
||||
from unittest.mock import patch, MagicMock
|
||||
from utils.news import fetch_rss
|
||||
|
||||
@ -176,7 +177,6 @@ class TestFetchRss:
|
||||
@patch("utils.news._session.get")
|
||||
async def test_fetch_rss_http_error(self, mock_get) -> None:
|
||||
"""HTTP-ошибка должна вернуть None."""
|
||||
import requests
|
||||
mock_get.side_effect = requests.exceptions.HTTPError("404 Not Found")
|
||||
result = await fetch_rss("https://example.com/rss")
|
||||
assert result is None
|
||||
@ -184,7 +184,6 @@ class TestFetchRss:
|
||||
@patch("utils.news._session.get")
|
||||
async def test_fetch_rss_connection_error(self, mock_get) -> None:
|
||||
"""Ошибка соединения должна вернуть None."""
|
||||
import requests
|
||||
mock_get.side_effect = requests.exceptions.ConnectionError("No connection")
|
||||
result = await fetch_rss("https://example.com/rss")
|
||||
assert result is None
|
||||
@ -192,7 +191,6 @@ class TestFetchRss:
|
||||
@patch("utils.news._session.get")
|
||||
async def test_fetch_rss_timeout(self, mock_get) -> None:
|
||||
"""Таймаут должен вернуть None."""
|
||||
import requests
|
||||
mock_get.side_effect = requests.exceptions.Timeout("Request timed out")
|
||||
result = await fetch_rss("https://example.com/rss")
|
||||
assert result is None
|
||||
@ -200,7 +198,6 @@ class TestFetchRss:
|
||||
@patch("utils.news._session.get")
|
||||
async def test_fetch_rss_ssl_error(self, mock_get) -> None:
|
||||
"""SSLError должен вернуть None."""
|
||||
import requests
|
||||
mock_get.side_effect = requests.exceptions.SSLError("SSL handshake failed")
|
||||
result = await fetch_rss("https://example.com/rss")
|
||||
assert result is None
|
||||
@ -294,7 +291,6 @@ class TestFetchRss:
|
||||
@patch("utils.news._session.get")
|
||||
async def test_fetch_rss_request_exception(self, mock_get) -> None:
|
||||
"""Общий RequestException должен вернуть None."""
|
||||
import requests
|
||||
mock_get.side_effect = requests.RequestException("Generic error")
|
||||
result = await fetch_rss("https://example.com/rss")
|
||||
assert result is None
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
|
||||
import pytest
|
||||
import requests
|
||||
from requests.exceptions import ConnectionError, Timeout, SSLError
|
||||
from unittest.mock import patch, MagicMock
|
||||
from utils.pogoda import fetch_weather, fetch_open_meteo
|
||||
|
||||
@ -20,7 +22,6 @@ class TestFetchWeather:
|
||||
@patch("utils.pogoda._session.get")
|
||||
async def test_fetch_weather_fallback_on_ssl_error(self, mock_get) -> None:
|
||||
"""SSLError на первой попытке → fallback на Open-Meteo."""
|
||||
from requests.exceptions import SSLError
|
||||
mock_get.side_effect = [
|
||||
SSLError("SSL Error"),
|
||||
MagicMock(json=MagicMock(return_value={"result": "fallback"})),
|
||||
@ -33,7 +34,6 @@ class TestFetchWeather:
|
||||
@patch("utils.pogoda._session.get")
|
||||
async def test_fetch_weather_fallback_on_connection_error(self, mock_get) -> None:
|
||||
"""ConnectionError → fallback на Open-Meteo."""
|
||||
from requests.exceptions import ConnectionError
|
||||
mock_get.side_effect = ConnectionError("No connection")
|
||||
with patch("utils.pogoda.fetch_open_meteo") as mock_fallback:
|
||||
mock_fallback.return_value = {"result": "fallback"}
|
||||
@ -43,7 +43,6 @@ class TestFetchWeather:
|
||||
@patch("utils.pogoda._session.get")
|
||||
async def test_fetch_weather_fallback_on_timeout(self, mock_get) -> None:
|
||||
"""Timeout → fallback на Open-Meteo."""
|
||||
from requests.exceptions import Timeout
|
||||
mock_get.side_effect = Timeout("Timed out")
|
||||
with patch("utils.pogoda.fetch_open_meteo") as mock_fallback:
|
||||
mock_fallback.return_value = {"result": "fallback"}
|
||||
@ -53,7 +52,6 @@ class TestFetchWeather:
|
||||
@patch("utils.pogoda._session.get")
|
||||
async def test_fetch_weather_all_retries_fail(self, mock_get) -> None:
|
||||
"""Все попытки не удались → fallback на Open-Meteo."""
|
||||
from requests.exceptions import ConnectionError
|
||||
mock_get.side_effect = ConnectionError("No connection")
|
||||
with patch("utils.pogoda.fetch_open_meteo") as mock_fallback:
|
||||
mock_fallback.return_value = None
|
||||
@ -63,7 +61,6 @@ class TestFetchWeather:
|
||||
@patch("utils.pogoda._session.get")
|
||||
async def test_fetch_weather_request_exception(self, mock_get) -> None:
|
||||
"""Общий RequestException → fallback на Open-Meteo."""
|
||||
import requests
|
||||
mock_get.side_effect = requests.RequestException("Generic error")
|
||||
with patch("utils.pogoda.fetch_open_meteo") as mock_fallback:
|
||||
mock_fallback.return_value = {"result": "fallback"}
|
||||
@ -135,7 +132,6 @@ class TestFetchOpenMeteo:
|
||||
@patch("utils.pogoda._session.get")
|
||||
async def test_fetch_open_meteo_ssl_error(self, mock_get) -> None:
|
||||
"""SSLError → вернуть None."""
|
||||
from requests.exceptions import SSLError
|
||||
mock_get.side_effect = SSLError("SSL Error")
|
||||
with patch("utils.pogoda.fetch_open_meteo") as mock_fallback:
|
||||
# Внутренний fallback тоже падает, проверяем что возвращается None
|
||||
@ -146,7 +142,6 @@ class TestFetchOpenMeteo:
|
||||
@patch("utils.pogoda._session.get")
|
||||
async def test_fetch_open_meteo_connection_error(self, mock_get) -> None:
|
||||
"""ConnectionError → вернуть None."""
|
||||
from requests.exceptions import ConnectionError
|
||||
mock_get.side_effect = ConnectionError("No connection")
|
||||
result = await fetch_open_meteo()
|
||||
assert result is None
|
||||
@ -154,7 +149,6 @@ class TestFetchOpenMeteo:
|
||||
@patch("utils.pogoda._session.get")
|
||||
async def test_fetch_open_meteo_timeout(self, mock_get) -> None:
|
||||
"""Timeout → вернуть None."""
|
||||
from requests.exceptions import Timeout
|
||||
mock_get.side_effect = Timeout("Timed out")
|
||||
result = await fetch_open_meteo()
|
||||
assert result is None
|
||||
@ -162,7 +156,6 @@ class TestFetchOpenMeteo:
|
||||
@patch("utils.pogoda._session.get")
|
||||
async def test_fetch_open_meteo_request_exception(self, mock_get) -> None:
|
||||
"""Общий RequestException → вернуть None."""
|
||||
import requests
|
||||
mock_get.side_effect = requests.RequestException("Error")
|
||||
result = await fetch_open_meteo()
|
||||
assert result is None
|
||||
@ -170,7 +163,6 @@ class TestFetchOpenMeteo:
|
||||
@patch("utils.pogoda._session.get")
|
||||
async def test_fetch_open_meteo_json_parse_error(self, mock_get) -> None:
|
||||
"""Ошибка парсинга JSON → вернуть None."""
|
||||
import requests
|
||||
mock_response = MagicMock()
|
||||
mock_response.json.side_effect = requests.RequestException("JSON Error")
|
||||
mock_response.raise_for_status = MagicMock()
|
||||
@ -181,7 +173,6 @@ class TestFetchOpenMeteo:
|
||||
@patch("utils.pogoda._session.get")
|
||||
async def test_fetch_open_meteo_retry_on_error(self, mock_get) -> None:
|
||||
"""Retry: первая попытка падает, вторая успешна."""
|
||||
from requests.exceptions import ConnectionError
|
||||
success_response = MagicMock()
|
||||
success_response.json.return_value = {"current": {"temperature": 20, "apparent_temperature": 18, "weather_code": 1, "wind_speed_10m": 4, "relative_humidity_2m": 60, "pressure_msl": 1015}}
|
||||
success_response.raise_for_status = MagicMock()
|
||||
@ -193,7 +184,6 @@ class TestFetchOpenMeteo:
|
||||
@patch("utils.pogoda._session.get")
|
||||
async def test_fetch_open_meteo_all_retries_fail(self, mock_get) -> None:
|
||||
"""Все попытки неудачны → None."""
|
||||
from requests.exceptions import ConnectionError
|
||||
mock_get.side_effect = [ConnectionError("fail"), ConnectionError("fail"), ConnectionError("fail")]
|
||||
result = await fetch_open_meteo(max_retries=3)
|
||||
assert result is None
|
||||
@ -202,7 +192,6 @@ class TestFetchOpenMeteo:
|
||||
@patch("utils.pogoda._session.get")
|
||||
async def test_fetch_open_meteo_http_error(self, mock_get) -> None:
|
||||
"""HTTP 404 → raise_for_status бросит исключение → None."""
|
||||
import requests
|
||||
mock_response = MagicMock()
|
||||
mock_response.raise_for_status.side_effect = requests.HTTPError("HTTP 404")
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
@ -3,6 +3,8 @@
|
||||
import pytest
|
||||
from unittest.mock import AsyncMock, MagicMock
|
||||
|
||||
from commands.help import Help
|
||||
|
||||
|
||||
class TestHelpCommandDiscord:
|
||||
"""Тесты для команды help на Discord."""
|
||||
@ -17,8 +19,6 @@ class TestHelpCommandDiscord:
|
||||
|
||||
async def test_show_help_sends_simple_text(self) -> None:
|
||||
"""Проверка, что команда отправляет простое текстовое сообщение."""
|
||||
from commands.help import Help
|
||||
|
||||
mock_ctx = MagicMock()
|
||||
mock_ctx.bot.commands = []
|
||||
mock_ctx.send = AsyncMock(return_value=None)
|
||||
@ -30,8 +30,6 @@ class TestHelpCommandDiscord:
|
||||
|
||||
async def test_show_help_message_content(self) -> None:
|
||||
"""Проверка содержания отправленного сообщения."""
|
||||
from commands.help import Help
|
||||
|
||||
message_calls = []
|
||||
|
||||
def send_side_effect(text: str, *args, **kwargs):
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
"""Тесты для utils/logger.py — проверка настройки логирования."""
|
||||
|
||||
import io
|
||||
import logging
|
||||
import logging.handlers
|
||||
import os
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
@ -43,8 +45,6 @@ def test_invalid_level_defaults_to_info() -> 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"
|
||||
@ -68,8 +68,6 @@ def test_file_handler_when_logs_dir_exists() -> None:
|
||||
|
||||
def test_logs_dir_created_automatically() -> None:
|
||||
"""Директория logs создаётся автоматически, если её нет."""
|
||||
import logging.handlers
|
||||
|
||||
orig_cwd = os.getcwd()
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
os.chdir(tmpdir)
|
||||
@ -109,9 +107,6 @@ def test_discord_level_is_info() -> None:
|
||||
|
||||
def test_log_message_format() -> None:
|
||||
"""Формат сообщения: время, уровень, имя модуля, текст."""
|
||||
import io
|
||||
import sys
|
||||
|
||||
with _isolated_logger() as root:
|
||||
# Replace stdout with our buffer
|
||||
buffer = io.StringIO()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user