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