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) 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"