Переименовать команду погоды: pogoda -> pg, удалить docker-audit.md
This commit is contained in:
parent
6e31a7e6ff
commit
0d605eea5a
@ -25,7 +25,7 @@ python bot.py
|
|||||||
|
|
||||||
| Команда | Описание | Формат вывода |
|
| Команда | Описание | Формат вывода |
|
||||||
|---------|----------|---------------|
|
|---------|----------|---------------|
|
||||||
| `!pogoda` | Погода в Магнитогорске | Температура, ощущается, описание, влажность, ветер, давление |
|
| `!pg` | Погода в Магнитогорске | Температура, ощущается, описание, влажность, ветер, давление |
|
||||||
| `!news` | Топ-5 статей и топ-5 новостей AI с Habr | Два блока: статьи и новости |
|
| `!news` | Топ-5 статей и топ-5 новостей AI с Habr | Два блока: статьи и новости |
|
||||||
| `!morning` | Погода + топ-5 статей + топ-5 новостей + котик | Embed: котик thumbnail, погода, статьи, новости |
|
| `!morning` | Погода + топ-5 статей + топ-5 новостей + котик | Embed: котик thumbnail, погода, статьи, новости |
|
||||||
| `!cat` | Случайный котик | Embed с изображением |
|
| `!cat` | Случайный котик | Embed с изображением |
|
||||||
@ -50,7 +50,7 @@ python bot.py
|
|||||||
|
|
||||||
## API и внешние сервисы
|
## API и внешние сервисы
|
||||||
|
|
||||||
### Погода (!pogoda, !morning)
|
### Погода (!pg, !morning)
|
||||||
- **Основной**: `wttr.in/Magnitogorsk` (бесплатный, без ключа)
|
- **Основной**: `wttr.in/Magnitogorsk` (бесплатный, без ключа)
|
||||||
- **Fallback**: `api.open-meteo.com` (бесплатный, без ключа)
|
- **Fallback**: `api.open-meteo.com` (бесплатный, без ключа)
|
||||||
- Retry: 3 попытки с экспоненциальной задержкой при SSL/Connection/Timeout ошибках
|
- Retry: 3 попытки с экспоненциальной задержкой при SSL/Connection/Timeout ошибках
|
||||||
@ -89,7 +89,7 @@ requests>=2.31.0
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Структура данных погоды
|
## Структура данных погоды
|
||||||
Команда `!pogoda` возвращает:
|
Команда `!pg` возвращает:
|
||||||
```
|
```
|
||||||
Температура: X°C (ощущается как Y°C)
|
Температура: X°C (ощущается как Y°C)
|
||||||
Описание: Z
|
Описание: Z
|
||||||
|
|||||||
10
README.md
10
README.md
@ -56,7 +56,7 @@ python bot.py
|
|||||||
bot.py # Точка входа, инициализация бота, console_input()
|
bot.py # Точка входа, инициализация бота, console_input()
|
||||||
commands/ # Discord команды (cogs)
|
commands/ # Discord команды (cogs)
|
||||||
__init__.py # ALL_COMMANDS — явные импорты
|
__init__.py # ALL_COMMANDS — явные импорты
|
||||||
pogoda.py # !pogoda — погода с retry + fallback
|
pg.py # !pg — погода с retry + fallback
|
||||||
news.py # !news — новости с Habr
|
news.py # !news — новости с Habr
|
||||||
cat.py # !cat — случайный котик
|
cat.py # !cat — случайный котик
|
||||||
morning.py # !morning — утренний дайджест
|
morning.py # !morning — утренний дайджест
|
||||||
@ -77,7 +77,7 @@ tests/ # pytest-тесты
|
|||||||
test_fetch_rss.py # fetch_rss
|
test_fetch_rss.py # fetch_rss
|
||||||
test_fetch_weather.py # fetch_weather, fetch_open_meteo
|
test_fetch_weather.py # fetch_weather, fetch_open_meteo
|
||||||
test_format_articles.py # truncate_title, parse_date, format_articles
|
test_format_articles.py # truncate_title, parse_date, format_articles
|
||||||
test_commands_pogoda.py # Pogoda cog
|
test_commands_pg.py # Pg cog
|
||||||
ISSUES.md # Задачи и баг-трекер проекта
|
ISSUES.md # Задачи и баг-трекер проекта
|
||||||
pytest.ini # Конфигурация pytest
|
pytest.ini # Конфигурация pytest
|
||||||
```
|
```
|
||||||
@ -109,13 +109,13 @@ python -m pytest tests/ -v
|
|||||||
| `test_fetch_rss.py` | `fetch_rss()` | 20 |
|
| `test_fetch_rss.py` | `fetch_rss()` | 20 |
|
||||||
| `test_fetch_weather.py` | `fetch_weather()`, `fetch_open_meteo()` | 20 |
|
| `test_fetch_weather.py` | `fetch_weather()`, `fetch_open_meteo()` | 20 |
|
||||||
| `test_format_articles.py` | `truncate_title()`, `parse_date()`, `format_articles()` | 24 |
|
| `test_format_articles.py` | `truncate_title()`, `parse_date()`, `format_articles()` | 24 |
|
||||||
| `test_commands_pogoda.py` | `Pogoda` cog, команда `!pogoda` | 13 |
|
| `test_commands_pg.py` | `Pg` cog, команда `!pg` | 13 |
|
||||||
|
|
||||||
**Итого: 180 тестов.**
|
**Итого: 180 тестов.**
|
||||||
|
|
||||||
## API и внешние сервисы
|
## API и внешние сервисы
|
||||||
|
|
||||||
### Погода (!pogoda, !morning)
|
### Погода (!pg, !morning)
|
||||||
- **Основной**: `wttr.in/Magnitogorsk` (бесплатный, без ключа)
|
- **Основной**: `wttr.in/Magnitogorsk` (бесплатный, без ключа)
|
||||||
- **Fallback**: `api.open-meteo.com` (бесплатный, без ключа)
|
- **Fallback**: `api.open-meteo.com` (бесплатный, без ключа)
|
||||||
- Retry: 3 попытки с экспоненциальной задержкой при SSL/Connection/Timeout ошибках
|
- Retry: 3 попытки с экспоненциальной задержкой при SSL/Connection/Timeout ошибках
|
||||||
@ -140,7 +140,7 @@ python -m pytest tests/ -v
|
|||||||
|
|
||||||
## Структура данных погоды
|
## Структура данных погоды
|
||||||
|
|
||||||
Команда `!pogoda` возвращает:
|
Команда `!pg` возвращает:
|
||||||
|
|
||||||
```
|
```
|
||||||
Температура: X°C (ощущается как Y°C)
|
Температура: X°C (ощущается как Y°C)
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
from .pogoda import Pogoda
|
from .pg import Pg
|
||||||
from .news import News
|
from .news import News
|
||||||
from .cat import Cat
|
from .cat import Cat
|
||||||
from .morning import Morning
|
from .morning import Morning
|
||||||
|
|
||||||
ALL_COMMANDS = [Pogoda, News, Cat, Morning]
|
ALL_COMMANDS = [Pg, News, Cat, Morning]
|
||||||
|
|||||||
@ -3,14 +3,14 @@ from discord.ext import commands
|
|||||||
from utils.pogoda import fetch_weather, fetch_open_meteo, wmo_to_russian, translate_weather, pressure_to_mmhg
|
from utils.pogoda import fetch_weather, fetch_open_meteo, wmo_to_russian, translate_weather, pressure_to_mmhg
|
||||||
|
|
||||||
|
|
||||||
class Pogoda(commands.Cog):
|
class Pg(commands.Cog):
|
||||||
"""Команда !pogoda — прогноз погоды для Магнитогорска"""
|
"""Команда !pg — прогноз погоды для Магнитогорска"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.api_url = "https://wttr.in/Magnitogorsk?format=j1&lang=ru"
|
self.api_url = "https://wttr.in/Magnitogorsk?format=j1&lang=ru"
|
||||||
|
|
||||||
@commands.command(name="pogoda")
|
@commands.command(name="pg")
|
||||||
async def pogoda(self, ctx):
|
async def pg(self, ctx):
|
||||||
data = await fetch_weather(self.api_url)
|
data = await fetch_weather(self.api_url)
|
||||||
if data is None:
|
if data is None:
|
||||||
return
|
return
|
||||||
@ -1,71 +0,0 @@
|
|||||||
# Docker Audit — Discord Bot
|
|
||||||
|
|
||||||
## ✅ Что уже есть
|
|
||||||
|
|
||||||
| Файл | Статус |
|
|
||||||
|--------------------|---------|
|
|
||||||
| Dockerfile | ✅ Есть |
|
|
||||||
| docker-compose.yml | ✅ Есть |
|
|
||||||
| .dockerignore | ✅ Есть |
|
|
||||||
| requirements.txt | ✅ Есть |
|
|
||||||
| .env.example | ✅ Есть |
|
|
||||||
|
|
||||||
## ⚠️ Что можно улучшить
|
|
||||||
|
|
||||||
### 1. Dockerfile копирует весь проект целиком
|
|
||||||
Включая тесты, `console_commands/`, `.git`, `.pytest_cache` и прочее.
|
|
||||||
Стоит копировать только нужное: `bot.py`, `commands/`, `utils/`.
|
|
||||||
|
|
||||||
### 2. Версия Python
|
|
||||||
Dockerfile использует `python:3.12-slim`, но локально Python 3.14.
|
|
||||||
Discord.py 2.3.2+ поддерживает 3.12+, но стоит убедиться, что зависимости совместимы.
|
|
||||||
Обновить базовый образ до `python:3.14-slim`.
|
|
||||||
|
|
||||||
### 3. Нет .env в контейнере
|
|
||||||
docker-compose передаёт переменные через `environment:`, это работает,
|
|
||||||
но бот при запуске читает `.env` через `python-dotenv`.
|
|
||||||
Если запустить контейнер без `.env` на хосте, `DISCORD_TOKEN` не подтянется.
|
|
||||||
Решение: копировать `.env` в образ или убедиться, что `environment:` в compose передаёт все нужные переменные.
|
|
||||||
|
|
||||||
### 4. Нет healthcheck
|
|
||||||
Нет способа проверить, жив ли бот через Docker.
|
|
||||||
|
|
||||||
### 5. Нет ARG для тега образа
|
|
||||||
Нет возможности указать версию Python при сборке.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📋 План улучшений
|
|
||||||
|
|
||||||
### 1. Dockerfile
|
|
||||||
- Обновить базовый образ до `python:3.14-slim`
|
|
||||||
- Добавить `ARG PYTHON_VERSION` для гибкости
|
|
||||||
- Оптимизировать COPY: копировать только `bot.py`, `commands/`, `utils/`
|
|
||||||
- Добавить `.env` в образ (или убедиться, что env vars передаются корректно)
|
|
||||||
- Добавить healthcheck
|
|
||||||
- Добавить `.dockerenv` или аналог для предотвращения копирования лишних файлов
|
|
||||||
|
|
||||||
### 2. docker-compose.yml
|
|
||||||
- Добавить healthcheck для сервиса бота
|
|
||||||
- Добавить volumes для логов
|
|
||||||
- Убедиться, что все переменные из `.env` передаются в контейнер
|
|
||||||
- Добавить restart policy
|
|
||||||
|
|
||||||
### 3. .dockerignore
|
|
||||||
- Добавить `.vscode/`
|
|
||||||
- Добавить `__pycache__/` (уже есть, проверить)
|
|
||||||
- Добавить `.pytest_cache/`
|
|
||||||
- Добавить `node_modules/`
|
|
||||||
- Добавить `*.pyc`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🚀 Приоритет
|
|
||||||
|
|
||||||
| Приоритет | Изменение | Почему |
|
|
||||||
|-----------|-----------|--------|
|
|
||||||
| 🔴 Высокий | Оптимизировать COPY | Уменьшает размер образа, ускоряет сборку |
|
|
||||||
| 🔴 Высокий | Добавить healthcheck | Мониторинг состояния бота |
|
|
||||||
| 🟡 Средний | Обновить Python до 3.14 | Актуальность, но не критично |
|
|
||||||
| 🟡 Средний | Добавить volumes для логов | Удобство отладки |
|
|
||||||
| 🟢 Низкий | Добавить ARG для тега | Удобство, но не обязательно |
|
|
||||||
@ -1,23 +1,23 @@
|
|||||||
import pytest
|
import pytest
|
||||||
from unittest.mock import AsyncMock, MagicMock, patch
|
from unittest.mock import AsyncMock, MagicMock, patch
|
||||||
|
|
||||||
from commands.pogoda import Pogoda
|
from commands.pg import Pg
|
||||||
|
|
||||||
|
|
||||||
class TestPogodaInit:
|
class TestPgInit:
|
||||||
"""Тесты инициализации Cog Pogoda."""
|
"""Тесты инициализации Cog Pg."""
|
||||||
|
|
||||||
def test_init_sets_api_url(self):
|
def test_init_sets_api_url(self):
|
||||||
"""__init__ должен устанавливать api_url."""
|
"""__init__ должен устанавливать api_url."""
|
||||||
cog = Pogoda()
|
cog = Pg()
|
||||||
assert cog.api_url == "https://wttr.in/Magnitogorsk?format=j1&lang=ru"
|
assert cog.api_url == "https://wttr.in/Magnitogorsk?format=j1&lang=ru"
|
||||||
|
|
||||||
|
|
||||||
class TestPogodaCommand:
|
class TestPgCommand:
|
||||||
"""Тесты команды !pogoda."""
|
"""Тесты команды !pogoda."""
|
||||||
|
|
||||||
def _make_cog(self):
|
def _make_cog(self):
|
||||||
return Pogoda()
|
return Pg()
|
||||||
|
|
||||||
def _make_ctx(self, send_return=None):
|
def _make_ctx(self, send_return=None):
|
||||||
ctx = MagicMock()
|
ctx = MagicMock()
|
||||||
@ -42,14 +42,14 @@ class TestPogodaCommand:
|
|||||||
return defaults
|
return defaults
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_pogoda_success(self):
|
async def test_pg_success(self):
|
||||||
"""Успешный запрос погоды должен отправить embed с данными."""
|
"""Успешный запрос погоды должен отправить embed с данными."""
|
||||||
cog = self._make_cog()
|
cog = self._make_cog()
|
||||||
ctx = self._make_ctx()
|
ctx = self._make_ctx()
|
||||||
weather = self._make_weather_data()
|
weather = self._make_weather_data()
|
||||||
|
|
||||||
with patch("commands.pogoda.fetch_weather", new=AsyncMock(return_value=weather)):
|
with patch("commands.pg.fetch_weather", new=AsyncMock(return_value=weather)):
|
||||||
await cog.pogoda.callback(cog, ctx)
|
await cog.pg.callback(cog, ctx)
|
||||||
|
|
||||||
ctx.send.assert_called_once()
|
ctx.send.assert_called_once()
|
||||||
args = ctx.send.call_args[0][0]
|
args = ctx.send.call_args[0][0]
|
||||||
@ -61,80 +61,80 @@ class TestPogodaCommand:
|
|||||||
assert "Давление: 759.8 мм рт. ст." in args
|
assert "Давление: 759.8 мм рт. ст." in args
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_pogoda_fetch_returns_none(self):
|
async def test_pg_fetch_returns_none(self):
|
||||||
"""fetch_weather вернул None — бот должен ничего не отправить."""
|
"""fetch_weather вернул None — бот должен ничего не отправить."""
|
||||||
cog = self._make_cog()
|
cog = self._make_cog()
|
||||||
ctx = self._make_ctx()
|
ctx = self._make_ctx()
|
||||||
|
|
||||||
with patch("commands.pogoda.fetch_weather", new=AsyncMock(return_value=None)):
|
with patch("commands.pg.fetch_weather", new=AsyncMock(return_value=None)):
|
||||||
await cog.pogoda.callback(cog, ctx)
|
await cog.pg.callback(cog, ctx)
|
||||||
|
|
||||||
ctx.send.assert_not_called()
|
ctx.send.assert_not_called()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_pogoda_empty_current_condition(self):
|
async def test_pg_empty_current_condition(self):
|
||||||
"""current_condition пустой список — код выбрасывает IndexError (баг в коде)."""
|
"""current_condition пустой список — код выбрасывает IndexError (баг в коде)."""
|
||||||
cog = self._make_cog()
|
cog = self._make_cog()
|
||||||
ctx = self._make_ctx()
|
ctx = self._make_ctx()
|
||||||
weather = {"current_condition": []}
|
weather = {"current_condition": []}
|
||||||
|
|
||||||
with patch("commands.pogoda.fetch_weather", new=AsyncMock(return_value=weather)):
|
with patch("commands.pg.fetch_weather", new=AsyncMock(return_value=weather)):
|
||||||
with pytest.raises(IndexError):
|
with pytest.raises(IndexError):
|
||||||
await cog.pogoda.callback(cog, ctx)
|
await cog.pg.callback(cog, ctx)
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_pogoda_current_condition_none(self):
|
async def test_pg_current_condition_none(self):
|
||||||
"""current_condition — пустой dict — бот должен сообщить об ошибке."""
|
"""current_condition — пустой dict — бот должен сообщить об ошибке."""
|
||||||
cog = self._make_cog()
|
cog = self._make_cog()
|
||||||
ctx = self._make_ctx()
|
ctx = self._make_ctx()
|
||||||
weather = {"current_condition": [{}]}
|
weather = {"current_condition": [{}]}
|
||||||
|
|
||||||
with patch("commands.pogoda.fetch_weather", new=AsyncMock(return_value=weather)):
|
with patch("commands.pg.fetch_weather", new=AsyncMock(return_value=weather)):
|
||||||
await cog.pogoda.callback(cog, ctx)
|
await cog.pg.callback(cog, ctx)
|
||||||
|
|
||||||
ctx.send.assert_called_once_with("Не удалось получить данные о погоде.")
|
ctx.send.assert_called_once_with("Не удалось получить данные о погоде.")
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_pogoda_wind_non_numeric(self):
|
async def test_pg_wind_non_numeric(self):
|
||||||
"""windspeedKmph — не число — wind должен быть '—'."""
|
"""windspeedKmph — не число — wind должен быть '—'."""
|
||||||
cog = self._make_cog()
|
cog = self._make_cog()
|
||||||
ctx = self._make_ctx()
|
ctx = self._make_ctx()
|
||||||
weather = self._make_weather_data(windspeedKmph="abc")
|
weather = self._make_weather_data(windspeedKmph="abc")
|
||||||
|
|
||||||
with patch("commands.pogoda.fetch_weather", new=AsyncMock(return_value=weather)):
|
with patch("commands.pg.fetch_weather", new=AsyncMock(return_value=weather)):
|
||||||
await cog.pogoda.callback(cog, ctx)
|
await cog.pg.callback(cog, ctx)
|
||||||
|
|
||||||
args = ctx.send.call_args[0][0]
|
args = ctx.send.call_args[0][0]
|
||||||
assert "Ветер: — м/с" in args
|
assert "Ветер: — м/с" in args
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_pogoda_wind_none(self):
|
async def test_pg_wind_none(self):
|
||||||
"""windspeedKmph отсутствует — wind должен быть '—'."""
|
"""windspeedKmph отсутствует — wind должен быть '—'."""
|
||||||
cog = self._make_cog()
|
cog = self._make_cog()
|
||||||
ctx = self._make_ctx()
|
ctx = self._make_ctx()
|
||||||
weather = self._make_weather_data(windspeedKmph=None)
|
weather = self._make_weather_data(windspeedKmph=None)
|
||||||
|
|
||||||
with patch("commands.pogoda.fetch_weather", new=AsyncMock(return_value=weather)):
|
with patch("commands.pg.fetch_weather", new=AsyncMock(return_value=weather)):
|
||||||
await cog.pogoda.callback(cog, ctx)
|
await cog.pg.callback(cog, ctx)
|
||||||
|
|
||||||
args = ctx.send.call_args[0][0]
|
args = ctx.send.call_args[0][0]
|
||||||
assert "Ветер: — м/с" in args
|
assert "Ветер: — м/с" in args
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_pogoda_zero_wind(self):
|
async def test_pg_zero_wind(self):
|
||||||
"""windspeedKmph = 0 — wind должен быть 0.0."""
|
"""windspeedKmph = 0 — wind должен быть 0.0."""
|
||||||
cog = self._make_cog()
|
cog = self._make_cog()
|
||||||
ctx = self._make_ctx()
|
ctx = self._make_ctx()
|
||||||
weather = self._make_weather_data(windspeedKmph="0")
|
weather = self._make_weather_data(windspeedKmph="0")
|
||||||
|
|
||||||
with patch("commands.pogoda.fetch_weather", new=AsyncMock(return_value=weather)):
|
with patch("commands.pg.fetch_weather", new=AsyncMock(return_value=weather)):
|
||||||
await cog.pogoda.callback(cog, ctx)
|
await cog.pg.callback(cog, ctx)
|
||||||
|
|
||||||
args = ctx.send.call_args[0][0]
|
args = ctx.send.call_args[0][0]
|
||||||
assert "Ветер: 0.0 м/с" in args
|
assert "Ветер: 0.0 м/с" in args
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_pogoda_default_values(self):
|
async def test_pg_default_values(self):
|
||||||
"""Поля с отсутствующими значениями должны давать '—'."""
|
"""Поля с отсутствующими значениями должны давать '—'."""
|
||||||
cog = self._make_cog()
|
cog = self._make_cog()
|
||||||
ctx = self._make_ctx()
|
ctx = self._make_ctx()
|
||||||
@ -146,8 +146,8 @@ class TestPogodaCommand:
|
|||||||
pressure=None,
|
pressure=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
with patch("commands.pogoda.fetch_weather", new=AsyncMock(return_value=weather)):
|
with patch("commands.pg.fetch_weather", new=AsyncMock(return_value=weather)):
|
||||||
await cog.pogoda.callback(cog, ctx)
|
await cog.pg.callback(cog, ctx)
|
||||||
|
|
||||||
args = ctx.send.call_args[0][0]
|
args = ctx.send.call_args[0][0]
|
||||||
# dict.get(key, default) возвращает None, если ключ есть, но значение None
|
# dict.get(key, default) возвращает None, если ключ есть, но значение None
|
||||||
@ -158,53 +158,53 @@ class TestPogodaCommand:
|
|||||||
assert "Давление: — мм рт. ст." in args
|
assert "Давление: — мм рт. ст." in args
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_pogoda_translate_unknown_weather(self):
|
async def test_pg_translate_unknown_weather(self):
|
||||||
"""Неизвестное описание погоды должно возвращать оригинал."""
|
"""Неизвестное описание погоды должно возвращать оригинал."""
|
||||||
cog = self._make_cog()
|
cog = self._make_cog()
|
||||||
ctx = self._make_ctx()
|
ctx = self._make_ctx()
|
||||||
weather = self._make_weather_data(weatherDesc=[{"value": "UnknownXYZ"}])
|
weather = self._make_weather_data(weatherDesc=[{"value": "UnknownXYZ"}])
|
||||||
|
|
||||||
with patch("commands.pogoda.fetch_weather", new=AsyncMock(return_value=weather)):
|
with patch("commands.pg.fetch_weather", new=AsyncMock(return_value=weather)):
|
||||||
await cog.pogoda.callback(cog, ctx)
|
await cog.pg.callback(cog, ctx)
|
||||||
|
|
||||||
args = ctx.send.call_args[0][0]
|
args = ctx.send.call_args[0][0]
|
||||||
assert "Описание: UnknownXYZ" in args
|
assert "Описание: UnknownXYZ" in args
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_pogoda_russian_weather_description(self):
|
async def test_pg_russian_weather_description(self):
|
||||||
"""Описание погоды на русском должно корректно переводиться."""
|
"""Описание погоды на русском должно корректно переводиться."""
|
||||||
cog = self._make_cog()
|
cog = self._make_cog()
|
||||||
ctx = self._make_ctx()
|
ctx = self._make_ctx()
|
||||||
weather = self._make_weather_data(weatherDesc=[{"value": "Переменная облачность"}])
|
weather = self._make_weather_data(weatherDesc=[{"value": "Переменная облачность"}])
|
||||||
|
|
||||||
with patch("commands.pogoda.fetch_weather", new=AsyncMock(return_value=weather)):
|
with patch("commands.pg.fetch_weather", new=AsyncMock(return_value=weather)):
|
||||||
await cog.pogoda.callback(cog, ctx)
|
await cog.pg.callback(cog, ctx)
|
||||||
|
|
||||||
args = ctx.send.call_args[0][0]
|
args = ctx.send.call_args[0][0]
|
||||||
assert "Описание: Переменная облачность" in args
|
assert "Описание: Переменная облачность" in args
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_pogoda_negative_pressure(self):
|
async def test_pg_negative_pressure(self):
|
||||||
"""Отрицательное давление должно конвертироваться."""
|
"""Отрицательное давление должно конвертироваться."""
|
||||||
cog = self._make_cog()
|
cog = self._make_cog()
|
||||||
ctx = self._make_ctx()
|
ctx = self._make_ctx()
|
||||||
weather = self._make_weather_data(pressure="-50")
|
weather = self._make_weather_data(pressure="-50")
|
||||||
|
|
||||||
with patch("commands.pogoda.fetch_weather", new=AsyncMock(return_value=weather)):
|
with patch("commands.pg.fetch_weather", new=AsyncMock(return_value=weather)):
|
||||||
await cog.pogoda.callback(cog, ctx)
|
await cog.pg.callback(cog, ctx)
|
||||||
|
|
||||||
args = ctx.send.call_args[0][0]
|
args = ctx.send.call_args[0][0]
|
||||||
assert "Давление: -37.5 мм рт. ст." in args
|
assert "Давление: -37.5 мм рт. ст." in args
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_pogoda_high_wind(self):
|
async def test_pg_high_wind(self):
|
||||||
"""Большая скорость ветра должна корректно округляться."""
|
"""Большая скорость ветра должна корректно округляться."""
|
||||||
cog = self._make_cog()
|
cog = self._make_cog()
|
||||||
ctx = self._make_ctx()
|
ctx = self._make_ctx()
|
||||||
weather = self._make_weather_data(windspeedKmph="123")
|
weather = self._make_weather_data(windspeedKmph="123")
|
||||||
|
|
||||||
with patch("commands.pogoda.fetch_weather", new=AsyncMock(return_value=weather)):
|
with patch("commands.pg.fetch_weather", new=AsyncMock(return_value=weather)):
|
||||||
await cog.pogoda.callback(cog, ctx)
|
await cog.pg.callback(cog, ctx)
|
||||||
|
|
||||||
args = ctx.send.call_args[0][0]
|
args = ctx.send.call_args[0][0]
|
||||||
assert "Ветер: 34.2 м/с" in args
|
assert "Ветер: 34.2 м/с" in args
|
||||||
Loading…
x
Reference in New Issue
Block a user