discordBot/tests/test_integration.py

157 lines
6.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.

"""Интеграционные тесты — проверка взаимодействия компонентов без внешних API.
Тестируют реальные объекты (без моков на уровне команд),
но мокают только сетевые вызовы.
"""
import pytest
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