Добавлены интеграционные тесты: загрузка когов, команды !cat и !stats, утилиты (9 тестов, итого 239)
This commit is contained in:
parent
a5ea2086cd
commit
a063abcd17
154
tests/test_integration.py
Normal file
154
tests/test_integration.py
Normal file
@ -0,0 +1,154 @@
|
||||
"""Интеграционные тесты — проверка взаимодействия компонентов без внешних API.
|
||||
|
||||
Тестируют реальные объекты (без моков на уровне команд),
|
||||
но мокают только сетевые вызовы.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import requests
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def loaded_bot():
|
||||
"""Загрузить все ког-модули в бота."""
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
|
||||
intents = discord.Intents.default()
|
||||
intents.message_content = True
|
||||
bot = commands.Bot(command_prefix="!", intents=intents)
|
||||
|
||||
from commands import ALL_COMMANDS
|
||||
for cog_class in ALL_COMMANDS:
|
||||
await bot.add_cog(cog_class())
|
||||
return bot
|
||||
|
||||
|
||||
class TestCogLoading:
|
||||
"""Проверка загрузки ког-модулей."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_all_cogs_load(self, loaded_bot) -> None:
|
||||
"""Все ког-модули должны загружаться без ошибок."""
|
||||
from commands import ALL_COMMANDS
|
||||
assert len(loaded_bot.cogs) == len(ALL_COMMANDS)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_commands_registered(self, loaded_bot) -> None:
|
||||
"""Все команды должны быть зарегистрированы."""
|
||||
command_names = {cmd.name for cmd in loaded_bot.commands if cmd.cog is not None}
|
||||
expected = {"pg", "nw", "morning", "cat", "hp", "stats", "status"}
|
||||
assert command_names == expected
|
||||
|
||||
|
||||
class TestCommandFlow:
|
||||
"""Проверка полного потока команд (без сетевых вызовов)."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_cat_command_success(self, loaded_bot) -> None:
|
||||
"""Команда !cat должна отправить embed с котиком при успешном ответе API."""
|
||||
with patch("commands.cat.fetch_cat", new_callable=AsyncMock) as mock_fetch:
|
||||
mock_fetch.return_value = "https://example.com/cat.jpg"
|
||||
|
||||
mock_ctx = MagicMock()
|
||||
mock_ctx.author.name = "TestUser"
|
||||
mock_ctx.send = AsyncMock(return_value=None)
|
||||
|
||||
cat_cmd = loaded_bot.get_command("cat")
|
||||
assert cat_cmd is not None
|
||||
await cat_cmd.callback(cat_cmd.cog, mock_ctx)
|
||||
|
||||
mock_ctx.send.assert_awaited_once()
|
||||
embed = mock_ctx.send.call_args[1]["embed"]
|
||||
assert embed.title == "Котик для тебя!"
|
||||
assert embed.image.url == "https://example.com/cat.jpg"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_cat_command_failure(self, loaded_bot) -> None:
|
||||
"""Команда !cat при ошибке API должна отправить fallback сообщение."""
|
||||
with patch("commands.cat.fetch_cat", new_callable=AsyncMock) as mock_fetch:
|
||||
mock_fetch.return_value = None
|
||||
|
||||
mock_ctx = MagicMock()
|
||||
mock_ctx.author.name = "TestUser"
|
||||
mock_ctx.send = AsyncMock(return_value=None)
|
||||
|
||||
cat_cmd = loaded_bot.get_command("cat")
|
||||
await cat_cmd.callback(cat_cmd.cog, mock_ctx)
|
||||
|
||||
mock_ctx.send.assert_awaited_once()
|
||||
content = mock_ctx.send.call_args[0][0]
|
||||
assert "Не удалось получить котика" in content
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_stats_command_flow(self) -> None:
|
||||
"""Команда !stats должна показать реальную статистику бота."""
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
|
||||
intents = discord.Intents.default()
|
||||
intents.message_content = True
|
||||
bot = commands.Bot(command_prefix="!", intents=intents)
|
||||
|
||||
from commands.stats import Stats
|
||||
await bot.add_cog(Stats())
|
||||
|
||||
mock_ctx = MagicMock()
|
||||
guild = MagicMock()
|
||||
guild.channels = [] # нет CategoryChannel
|
||||
guild.member_count = 100
|
||||
mock_ctx.bot = MagicMock()
|
||||
mock_ctx.bot.guilds = [guild]
|
||||
mock_ctx.bot.latency = 0.050
|
||||
mock_ctx.send = AsyncMock(return_value=None)
|
||||
|
||||
stats_cmd = bot.get_command("stats")
|
||||
await stats_cmd.callback(stats_cmd.cog, mock_ctx)
|
||||
|
||||
mock_ctx.send.assert_awaited_once()
|
||||
embed = mock_ctx.send.call_args[1]["embed"]
|
||||
fields = {f.name: f.value for f in embed.fields}
|
||||
assert fields["Серверов"] == "1"
|
||||
assert fields["Каналов"] == "0"
|
||||
assert fields["Пользователей"] == "100"
|
||||
assert fields["Пинг"] == "50.0 мс"
|
||||
|
||||
|
||||
class TestUtilityFunctions:
|
||||
"""Проверка утилит без моков."""
|
||||
|
||||
def test_wmo_codes_mapping(self) -> None:
|
||||
"""wmo_to_russian должен переводить известные коды."""
|
||||
from utils.pogoda import wmo_to_russian
|
||||
|
||||
assert wmo_to_russian(0) == "Ясно"
|
||||
assert wmo_to_russian(3) == "Пасмурно"
|
||||
assert wmo_to_russian(51) == "Лёгкая морось"
|
||||
assert wmo_to_russian(None) == "Неизвестно"
|
||||
|
||||
def test_pressure_conversion(self) -> None:
|
||||
"""pressure_to_mmhg должен конвертировать mb в мм рт. ст."""
|
||||
from utils.pogoda import pressure_to_mmhg
|
||||
|
||||
assert pressure_to_mmhg(1013) == 759.8 # 1013 * 0.750062 = 759.81
|
||||
assert pressure_to_mmhg("—") == "—"
|
||||
assert pressure_to_mmhg(None) == "—"
|
||||
|
||||
def test_title_truncation(self) -> None:
|
||||
"""truncate_title должен обрезать длинные заголовки."""
|
||||
from utils.news import truncate_title
|
||||
|
||||
short = "Короткий заголовок"
|
||||
assert truncate_title(short) == short
|
||||
long_title = "A" * 100
|
||||
assert len(truncate_title(long_title, 20)) == 23 # 17 + "..."
|
||||
|
||||
def test_embed_text_truncation(self) -> None:
|
||||
"""truncate_embed_text должен обрезать до 4096 символов."""
|
||||
from utils.news import truncate_embed_text, truncate_embed_field
|
||||
|
||||
long_text = "A" * 5000
|
||||
assert len(truncate_embed_text(long_text)) == 4096
|
||||
assert truncate_embed_text(long_text).endswith("...")
|
||||
assert len(truncate_embed_field(long_text)) == 1024
|
||||
Loading…
x
Reference in New Issue
Block a user