304 lines
11 KiB
Python
304 lines
11 KiB
Python
from unittest.mock import AsyncMock, Mock, patch
|
|
|
|
import pytest
|
|
from aiogram import types
|
|
from aiogram.fsm.context import FSMContext
|
|
|
|
from helper_bot.handlers.admin.exceptions import (
|
|
InvalidInputError,
|
|
UserAlreadyBannedError,
|
|
UserNotFoundError,
|
|
)
|
|
from helper_bot.handlers.admin.services import AdminService, BannedUser, User
|
|
|
|
|
|
class TestAdminService:
|
|
"""Тесты для AdminService"""
|
|
|
|
def setup_method(self):
|
|
"""Настройка перед каждым тестом"""
|
|
self.mock_db = Mock()
|
|
self.admin_service = AdminService(self.mock_db)
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_last_users_success(self):
|
|
"""Тест успешного получения списка последних пользователей"""
|
|
# Arrange
|
|
# Формат данных: кортежи (full_name, user_id) как возвращает БД
|
|
mock_users_data = [
|
|
("User One", 1), # (full_name, user_id)
|
|
("User Two", 2), # (full_name, user_id)
|
|
]
|
|
self.mock_db.get_last_users = AsyncMock(return_value=mock_users_data)
|
|
|
|
# Act
|
|
result = await self.admin_service.get_last_users()
|
|
|
|
# Assert
|
|
assert len(result) == 2
|
|
assert result[0].user_id == 1
|
|
assert result[0].username == "Неизвестно" # username не возвращается из БД
|
|
assert result[0].full_name == "User One"
|
|
assert result[1].user_id == 2
|
|
assert result[1].username == "Неизвестно" # username не возвращается из БД
|
|
assert result[1].full_name == "User Two"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_user_by_username_success(self):
|
|
"""Тест успешного получения пользователя по username"""
|
|
# Arrange
|
|
user_id = 123
|
|
username = "test_user"
|
|
full_name = "Test User"
|
|
self.mock_db.get_user_id_by_username = AsyncMock(return_value=user_id)
|
|
self.mock_db.get_full_name_by_id = AsyncMock(return_value=full_name)
|
|
|
|
# Act
|
|
result = await self.admin_service.get_user_by_username(username)
|
|
|
|
# Assert
|
|
assert result is not None
|
|
assert result.user_id == user_id
|
|
assert result.username == username
|
|
assert result.full_name == full_name
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_user_by_username_not_found(self):
|
|
"""Тест получения пользователя по несуществующему username"""
|
|
# Arrange
|
|
username = "nonexistent_user"
|
|
self.mock_db.get_user_id_by_username = AsyncMock(return_value=None)
|
|
|
|
# Act
|
|
result = await self.admin_service.get_user_by_username(username)
|
|
|
|
# Assert
|
|
assert result is None
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_user_by_id_success(self):
|
|
"""Тест успешного получения пользователя по ID"""
|
|
# Arrange
|
|
user_id = 123
|
|
from database.models import User as DBUser
|
|
|
|
user_info = DBUser(
|
|
user_id=user_id,
|
|
first_name="Test",
|
|
full_name="Test User",
|
|
username="test_user",
|
|
)
|
|
self.mock_db.get_user_by_id = AsyncMock(return_value=user_info)
|
|
|
|
# Act
|
|
result = await self.admin_service.get_user_by_id(user_id)
|
|
|
|
# Assert
|
|
assert result is not None
|
|
assert result.user_id == user_id
|
|
assert result.username == "test_user"
|
|
assert result.full_name == "Test User"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_user_by_id_not_found(self):
|
|
"""Тест получения пользователя по несуществующему ID"""
|
|
# Arrange
|
|
user_id = 999
|
|
self.mock_db.get_user_by_id = AsyncMock(return_value=None)
|
|
|
|
# Act
|
|
result = await self.admin_service.get_user_by_id(user_id)
|
|
|
|
# Assert
|
|
assert result is None
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_validate_user_input_success(self):
|
|
"""Тест успешной валидации ID пользователя"""
|
|
# Act
|
|
result = await self.admin_service.validate_user_input("123")
|
|
|
|
# Assert
|
|
assert result == 123
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_validate_user_input_invalid_number(self):
|
|
"""Тест валидации некорректного ID"""
|
|
# Act & Assert
|
|
with pytest.raises(
|
|
InvalidInputError, match="ID пользователя должен быть числом"
|
|
):
|
|
await self.admin_service.validate_user_input("abc")
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_validate_user_input_negative_number(self):
|
|
"""Тест валидации отрицательного ID"""
|
|
# Act & Assert
|
|
with pytest.raises(
|
|
InvalidInputError, match="ID пользователя должен быть положительным числом"
|
|
):
|
|
await self.admin_service.validate_user_input("-1")
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_validate_user_input_zero(self):
|
|
"""Тест валидации нулевого ID"""
|
|
# Act & Assert
|
|
with pytest.raises(
|
|
InvalidInputError, match="ID пользователя должен быть положительным числом"
|
|
):
|
|
await self.admin_service.validate_user_input("0")
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_ban_user_success(self):
|
|
"""Тест успешной блокировки пользователя"""
|
|
# Arrange
|
|
user_id = 123
|
|
username = "test_user"
|
|
reason = "Test ban"
|
|
ban_days = 7
|
|
|
|
self.mock_db.check_user_in_blacklist = AsyncMock(return_value=False)
|
|
self.mock_db.set_user_blacklist = AsyncMock(return_value=None)
|
|
|
|
# Act
|
|
await self.admin_service.ban_user(
|
|
user_id, username, reason, ban_days, ban_author_id=999
|
|
)
|
|
|
|
# Assert
|
|
self.mock_db.check_user_in_blacklist.assert_called_once_with(user_id)
|
|
self.mock_db.set_user_blacklist.assert_called_once()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_ban_user_already_banned(self):
|
|
"""Тест попытки заблокировать уже заблокированного пользователя"""
|
|
# Arrange
|
|
user_id = 123
|
|
username = "test_user"
|
|
reason = "Test ban"
|
|
ban_days = 7
|
|
|
|
self.mock_db.check_user_in_blacklist = AsyncMock(return_value=True)
|
|
|
|
# Act & Assert
|
|
with pytest.raises(
|
|
UserAlreadyBannedError, match=f"Пользователь {user_id} уже заблокирован"
|
|
):
|
|
await self.admin_service.ban_user(
|
|
user_id, username, reason, ban_days, ban_author_id=999
|
|
)
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_ban_user_permanent(self):
|
|
"""Тест постоянной блокировки пользователя"""
|
|
# Arrange
|
|
user_id = 123
|
|
username = "test_user"
|
|
reason = "Permanent ban"
|
|
ban_days = None
|
|
|
|
self.mock_db.check_user_in_blacklist = AsyncMock(return_value=False)
|
|
self.mock_db.set_user_blacklist = AsyncMock(return_value=None)
|
|
|
|
# Act
|
|
await self.admin_service.ban_user(
|
|
user_id, username, reason, ban_days, ban_author_id=999
|
|
)
|
|
|
|
# Assert
|
|
self.mock_db.set_user_blacklist.assert_called_once_with(
|
|
user_id, None, reason, None, ban_author=999
|
|
)
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_unban_user_success(self):
|
|
"""Тест успешной разблокировки пользователя"""
|
|
# Arrange
|
|
user_id = 123
|
|
self.mock_db.delete_user_blacklist = AsyncMock(return_value=None)
|
|
|
|
# Act
|
|
await self.admin_service.unban_user(user_id)
|
|
|
|
# Assert
|
|
self.mock_db.delete_user_blacklist.assert_called_once_with(user_id)
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_banned_users_success(self):
|
|
"""Тест успешного получения списка забаненных пользователей."""
|
|
self.mock_db.get_banned_users_from_db = AsyncMock(
|
|
return_value=[(1, "спам", None), (2, "оскорбления", "2025-02-01")]
|
|
)
|
|
self.mock_db.get_username = AsyncMock(side_effect=["user1", "user2"])
|
|
self.mock_db.get_full_name_by_id = AsyncMock(side_effect=["Name One", "Name Two"])
|
|
|
|
result = await self.admin_service.get_banned_users()
|
|
|
|
assert len(result) == 2
|
|
assert result[0].user_id == 1
|
|
assert result[0].reason == "спам"
|
|
assert result[0].unban_date is None
|
|
assert result[1].user_id == 2
|
|
assert result[1].reason == "оскорбления"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_banned_users_uses_user_id_fallback(self):
|
|
"""get_banned_users при отсутствии username/full_name использует User_{id}."""
|
|
self.mock_db.get_banned_users_from_db = AsyncMock(return_value=[(99, "reason", None)])
|
|
self.mock_db.get_username = AsyncMock(return_value=None)
|
|
self.mock_db.get_full_name_by_id = AsyncMock(return_value=None)
|
|
|
|
result = await self.admin_service.get_banned_users()
|
|
|
|
assert len(result) == 1
|
|
assert result[0].username == "User_99"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_banned_users_for_display_success(self):
|
|
"""Тест успешного получения данных для отображения забаненных."""
|
|
with patch("helper_bot.handlers.admin.services.get_banned_users_list", new_callable=AsyncMock) as mock_list:
|
|
with patch("helper_bot.handlers.admin.services.get_banned_users_buttons", new_callable=AsyncMock) as mock_buttons:
|
|
mock_list.return_value = "Список забаненных"
|
|
mock_buttons.return_value = []
|
|
|
|
text, buttons = await self.admin_service.get_banned_users_for_display(0)
|
|
|
|
assert text == "Список забаненных"
|
|
assert buttons == []
|
|
mock_list.assert_awaited_once_with(0, self.mock_db)
|
|
mock_buttons.assert_awaited_once_with(self.mock_db)
|
|
|
|
|
|
class TestUser:
|
|
"""Тесты для модели User"""
|
|
|
|
def test_user_creation(self):
|
|
"""Тест создания объекта User"""
|
|
# Act
|
|
user = User(user_id=123, username="test_user", full_name="Test User")
|
|
|
|
# Assert
|
|
assert user.user_id == 123
|
|
assert user.username == "test_user"
|
|
assert user.full_name == "Test User"
|
|
|
|
|
|
class TestBannedUser:
|
|
"""Тесты для модели BannedUser"""
|
|
|
|
def test_banned_user_creation(self):
|
|
"""Тест создания объекта BannedUser"""
|
|
# Act
|
|
banned_user = BannedUser(
|
|
user_id=123,
|
|
username="test_user",
|
|
reason="Test ban",
|
|
unban_date="2025-01-01",
|
|
)
|
|
|
|
# Assert
|
|
assert banned_user.user_id == 123
|
|
assert banned_user.username == "test_user"
|
|
assert banned_user.reason == "Test ban"
|
|
assert banned_user.unban_date == "2025-01-01"
|