discordBot/tests/test_commands_pogoda.py
deadzilla 4b9bb7e97a Добавить pytest-тесты и конфигурацию
- pytest.ini для конфигурации тестов
- tests/test_pogoda.py — тесты translate_weather, pressure_to_mmhg, wmo_to_russian (93 теста)
- tests/test_fetch_cat.py — тесты fetch_cat (10 тестов)
- tests/test_fetch_rss.py — тесты fetch_rss (20 тестов)
- tests/test_format_articles.py — тесты truncate_title, parse_date, format_articles (24 теста)
- tests/test_fetch_weather.py — тесты fetch_weather, fetch_open_meteo (20 тестов)
- tests/test_commands_pogoda.py — тесты команды !pogoda (13 тестов)
- Обновить AGENTS.md и requirements.txt
2026-05-29 15:45:56 +05:00

211 lines
8.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import pytest
from unittest.mock import AsyncMock, MagicMock, patch
from commands.pogoda import Pogoda
class TestPogodaInit:
"""Тесты инициализации Cog Pogoda."""
def test_init_sets_api_url(self):
"""__init__ должен устанавливать api_url."""
cog = Pogoda()
assert cog.api_url == "https://wttr.in/Magnitogorsk?format=j1&lang=ru"
class TestPogodaCommand:
"""Тесты команды !pogoda."""
def _make_cog(self):
return Pogoda()
def _make_ctx(self, send_return=None):
ctx = MagicMock()
ctx.send = AsyncMock(return_value=send_return)
return ctx
def _make_weather_data(self, **extra):
"""Создать mock weather data с дефолтными полями."""
defaults = {
"current_condition": [
{
"temp_C": "22",
"FeelsLikeC": "24",
"weatherDesc": [{"value": "Clear"}],
"humidity": "45",
"windspeedKmph": "18",
"pressure": "1013",
}
]
}
defaults["current_condition"][0].update(extra)
return defaults
@pytest.mark.asyncio
async def test_pogoda_success(self):
"""Успешный запрос погоды должен отправить embed с данными."""
cog = self._make_cog()
ctx = self._make_ctx()
weather = self._make_weather_data()
with patch("commands.pogoda.fetch_weather", new=AsyncMock(return_value=weather)):
await cog.pogoda.callback(cog, ctx)
ctx.send.assert_called_once()
args = ctx.send.call_args[0][0]
assert "Температура: 22°C" in args
assert "(ощущается как 24°C)" in args
assert "Описание: Ясно" in args
assert "Влажность: 45%" in args
assert "Ветер: 5.0 м/с" in args
assert "Давление: 759.8 мм рт. ст." in args
@pytest.mark.asyncio
async def test_pogoda_fetch_returns_none(self):
"""fetch_weather вернул None — бот должен ничего не отправить."""
cog = self._make_cog()
ctx = self._make_ctx()
with patch("commands.pogoda.fetch_weather", new=AsyncMock(return_value=None)):
await cog.pogoda.callback(cog, ctx)
ctx.send.assert_not_called()
@pytest.mark.asyncio
async def test_pogoda_empty_current_condition(self):
"""current_condition пустой список — код выбрасывает IndexError (баг в коде)."""
cog = self._make_cog()
ctx = self._make_ctx()
weather = {"current_condition": []}
with patch("commands.pogoda.fetch_weather", new=AsyncMock(return_value=weather)):
with pytest.raises(IndexError):
await cog.pogoda.callback(cog, ctx)
@pytest.mark.asyncio
async def test_pogoda_current_condition_none(self):
"""current_condition — пустой dict — бот должен сообщить об ошибке."""
cog = self._make_cog()
ctx = self._make_ctx()
weather = {"current_condition": [{}]}
with patch("commands.pogoda.fetch_weather", new=AsyncMock(return_value=weather)):
await cog.pogoda.callback(cog, ctx)
ctx.send.assert_called_once_with("Не удалось получить данные о погоде.")
@pytest.mark.asyncio
async def test_pogoda_wind_non_numeric(self):
"""windspeedKmph — не число — wind должен быть ''."""
cog = self._make_cog()
ctx = self._make_ctx()
weather = self._make_weather_data(windspeedKmph="abc")
with patch("commands.pogoda.fetch_weather", new=AsyncMock(return_value=weather)):
await cog.pogoda.callback(cog, ctx)
args = ctx.send.call_args[0][0]
assert "Ветер: — м/с" in args
@pytest.mark.asyncio
async def test_pogoda_wind_none(self):
"""windspeedKmph отсутствует — wind должен быть ''."""
cog = self._make_cog()
ctx = self._make_ctx()
weather = self._make_weather_data(windspeedKmph=None)
with patch("commands.pogoda.fetch_weather", new=AsyncMock(return_value=weather)):
await cog.pogoda.callback(cog, ctx)
args = ctx.send.call_args[0][0]
assert "Ветер: — м/с" in args
@pytest.mark.asyncio
async def test_pogoda_zero_wind(self):
"""windspeedKmph = 0 — wind должен быть 0.0."""
cog = self._make_cog()
ctx = self._make_ctx()
weather = self._make_weather_data(windspeedKmph="0")
with patch("commands.pogoda.fetch_weather", new=AsyncMock(return_value=weather)):
await cog.pogoda.callback(cog, ctx)
args = ctx.send.call_args[0][0]
assert "Ветер: 0.0 м/с" in args
@pytest.mark.asyncio
async def test_pogoda_default_values(self):
"""Поля с отсутствующими значениями должны давать ''."""
cog = self._make_cog()
ctx = self._make_ctx()
weather = self._make_weather_data(
temp_C=None,
FeelsLikeC=None,
weatherDesc=[{"value": None}],
humidity=None,
pressure=None,
)
with patch("commands.pogoda.fetch_weather", new=AsyncMock(return_value=weather)):
await cog.pogoda.callback(cog, ctx)
args = ctx.send.call_args[0][0]
# dict.get(key, default) возвращает None, если ключ есть, но значение None
assert "Температура: None°C" in args
assert "ощущается как None°C" in args
assert "Описание: —" in args
assert "Влажность: None%" in args
assert "Давление: — мм рт. ст." in args
@pytest.mark.asyncio
async def test_pogoda_translate_unknown_weather(self):
"""Неизвестное описание погоды должно возвращать оригинал."""
cog = self._make_cog()
ctx = self._make_ctx()
weather = self._make_weather_data(weatherDesc=[{"value": "UnknownXYZ"}])
with patch("commands.pogoda.fetch_weather", new=AsyncMock(return_value=weather)):
await cog.pogoda.callback(cog, ctx)
args = ctx.send.call_args[0][0]
assert "Описание: UnknownXYZ" in args
@pytest.mark.asyncio
async def test_pogoda_russian_weather_description(self):
"""Описание погоды на русском должно корректно переводиться."""
cog = self._make_cog()
ctx = self._make_ctx()
weather = self._make_weather_data(weatherDesc=[{"value": "Переменная облачность"}])
with patch("commands.pogoda.fetch_weather", new=AsyncMock(return_value=weather)):
await cog.pogoda.callback(cog, ctx)
args = ctx.send.call_args[0][0]
assert "Описание: Переменная облачность" in args
@pytest.mark.asyncio
async def test_pogoda_negative_pressure(self):
"""Отрицательное давление должно конвертироваться."""
cog = self._make_cog()
ctx = self._make_ctx()
weather = self._make_weather_data(pressure="-50")
with patch("commands.pogoda.fetch_weather", new=AsyncMock(return_value=weather)):
await cog.pogoda.callback(cog, ctx)
args = ctx.send.call_args[0][0]
assert "Давление: -37.5 мм рт. ст." in args
@pytest.mark.asyncio
async def test_pogoda_high_wind(self):
"""Большая скорость ветра должна корректно округляться."""
cog = self._make_cog()
ctx = self._make_ctx()
weather = self._make_weather_data(windspeedKmph="123")
with patch("commands.pogoda.fetch_weather", new=AsyncMock(return_value=weather)):
await cog.pogoda.callback(cog, ctx)
args = ctx.send.call_args[0][0]
assert "Ветер: 34.2 м/с" in args