Files
telegram-helper-bot/tests/test_refactored_admin_handlers.py
Andrey e2b1353408 feat: добавлена система миграций БД и CI/CD пайплайны
- Создана система отслеживания миграций (MigrationRepository, таблица migrations)
- Добавлен скрипт apply_migrations.py для автоматического применения миграций
- Созданы CI/CD пайплайны (.github/workflows/ci.yml, deploy.yml)
- Обновлена документация по миграциям в database-patterns.md
- Миграции применяются автоматически при деплое в продакшн
2026-01-25 23:17:09 +03:00

239 lines
9.0 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)
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"