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