427 lines
22 KiB
Python
427 lines
22 KiB
Python
import pytest
|
||
from unittest.mock import Mock, AsyncMock, patch, MagicMock
|
||
from datetime import datetime
|
||
import time
|
||
|
||
from database.repositories.blacklist_repository import BlacklistRepository
|
||
from database.models import BlacklistUser
|
||
|
||
|
||
class TestBlacklistRepository:
|
||
"""Тесты для BlacklistRepository"""
|
||
|
||
@pytest.fixture
|
||
def mock_db_connection(self):
|
||
"""Мок для DatabaseConnection"""
|
||
mock_connection = Mock()
|
||
mock_connection._execute_query = AsyncMock()
|
||
mock_connection._execute_query_with_result = AsyncMock()
|
||
mock_connection.logger = Mock()
|
||
return mock_connection
|
||
|
||
@pytest.fixture
|
||
def blacklist_repository(self, mock_db_connection):
|
||
"""Экземпляр BlacklistRepository для тестов"""
|
||
# Патчим наследование от DatabaseConnection
|
||
with patch.object(BlacklistRepository, '__init__', return_value=None):
|
||
repo = BlacklistRepository()
|
||
repo._execute_query = mock_db_connection._execute_query
|
||
repo._execute_query_with_result = mock_db_connection._execute_query_with_result
|
||
repo.logger = mock_db_connection.logger
|
||
return repo
|
||
|
||
@pytest.fixture
|
||
def sample_blacklist_user(self):
|
||
"""Тестовый пользователь в черном списке"""
|
||
return BlacklistUser(
|
||
user_id=12345,
|
||
message_for_user="Нарушение правил",
|
||
date_to_unban=int(time.time()) + 86400, # +1 день
|
||
created_at=int(time.time()),
|
||
ban_author=999,
|
||
)
|
||
|
||
@pytest.fixture
|
||
def sample_blacklist_user_permanent(self):
|
||
"""Тестовый пользователь с постоянным баном"""
|
||
return BlacklistUser(
|
||
user_id=67890,
|
||
message_for_user="Постоянный бан",
|
||
date_to_unban=None,
|
||
created_at=int(time.time()),
|
||
ban_author=None,
|
||
)
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_create_tables(self, blacklist_repository):
|
||
"""Тест создания таблицы черного списка"""
|
||
await blacklist_repository.create_tables()
|
||
|
||
# Проверяем, что метод вызван
|
||
blacklist_repository._execute_query.assert_called()
|
||
calls = blacklist_repository._execute_query.call_args_list
|
||
|
||
# Проверяем, что создается таблица с правильной структурой
|
||
create_table_call = calls[0]
|
||
assert "CREATE TABLE IF NOT EXISTS blacklist" in create_table_call[0][0]
|
||
assert "user_id INTEGER NOT NULL PRIMARY KEY" in create_table_call[0][0]
|
||
assert "message_for_user TEXT" in create_table_call[0][0]
|
||
assert "date_to_unban INTEGER" in create_table_call[0][0]
|
||
assert "created_at INTEGER DEFAULT (strftime('%s', 'now'))" in create_table_call[0][0]
|
||
assert "FOREIGN KEY (user_id) REFERENCES our_users (user_id) ON DELETE CASCADE" in create_table_call[0][0]
|
||
|
||
# Проверяем логирование
|
||
blacklist_repository.logger.info.assert_called_once_with("Таблица черного списка создана")
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_add_user(self, blacklist_repository, sample_blacklist_user):
|
||
"""Тест добавления пользователя в черный список"""
|
||
await blacklist_repository.add_user(sample_blacklist_user)
|
||
|
||
# Проверяем, что метод вызван с правильными параметрами
|
||
blacklist_repository._execute_query.assert_called_once()
|
||
call_args = blacklist_repository._execute_query.call_args
|
||
|
||
# Проверяем SQL запрос (учитываем форматирование)
|
||
sql_query = call_args[0][0].replace('\n', ' ').replace(' ', ' ').replace(' ', ' ').strip()
|
||
expected_sql = "INSERT INTO blacklist (user_id, message_for_user, date_to_unban, ban_author) VALUES (?, ?, ?, ?)"
|
||
assert sql_query == expected_sql
|
||
|
||
# Проверяем параметры
|
||
assert call_args[0][1] == (12345, "Нарушение правил", sample_blacklist_user.date_to_unban, 999)
|
||
|
||
# Проверяем логирование
|
||
blacklist_repository.logger.info.assert_called_once_with(
|
||
"Пользователь добавлен в черный список: user_id=12345"
|
||
)
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_add_user_permanent_ban(self, blacklist_repository, sample_blacklist_user_permanent):
|
||
"""Тест добавления пользователя с постоянным баном"""
|
||
await blacklist_repository.add_user(sample_blacklist_user_permanent)
|
||
|
||
call_args = blacklist_repository._execute_query.call_args
|
||
assert call_args[0][1] == (67890, "Постоянный бан", None, None)
|
||
|
||
blacklist_repository.logger.info.assert_called_once_with(
|
||
"Пользователь добавлен в черный список: user_id=67890"
|
||
)
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_remove_user_success(self, blacklist_repository):
|
||
"""Тест успешного удаления пользователя из черного списка"""
|
||
await blacklist_repository.remove_user(12345)
|
||
|
||
# Проверяем, что метод вызван с правильными параметрами
|
||
blacklist_repository._execute_query.assert_called_once()
|
||
call_args = blacklist_repository._execute_query.call_args
|
||
|
||
assert call_args[0][0] == "DELETE FROM blacklist WHERE user_id = ?"
|
||
assert call_args[0][1] == (12345,)
|
||
|
||
# Проверяем логирование
|
||
blacklist_repository.logger.info.assert_called_once_with(
|
||
"Пользователь с идентификатором 12345 успешно удален из черного списка."
|
||
)
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_remove_user_failure(self, blacklist_repository):
|
||
"""Тест неудачного удаления пользователя из черного списка"""
|
||
# Симулируем ошибку при удалении
|
||
blacklist_repository._execute_query.side_effect = Exception("Database error")
|
||
|
||
result = await blacklist_repository.remove_user(12345)
|
||
|
||
# Проверяем, что возвращается False при ошибке
|
||
assert result is False
|
||
|
||
# Проверяем логирование ошибки
|
||
blacklist_repository.logger.error.assert_called_once()
|
||
error_log = blacklist_repository.logger.error.call_args[0][0]
|
||
assert "Ошибка удаления пользователя с идентификатором 12345" in error_log
|
||
assert "Database error" in error_log
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_user_exists_true(self, blacklist_repository):
|
||
"""Тест проверки существования пользователя (пользователь существует)"""
|
||
# Симулируем результат запроса - пользователь найден
|
||
blacklist_repository._execute_query_with_result.return_value = [(1,)]
|
||
|
||
result = await blacklist_repository.user_exists(12345)
|
||
|
||
# Проверяем, что возвращается True
|
||
assert result is True
|
||
|
||
# Проверяем, что метод вызван с правильными параметрами
|
||
blacklist_repository._execute_query_with_result.assert_called_once()
|
||
call_args = blacklist_repository._execute_query_with_result.call_args
|
||
|
||
assert call_args[0][0] == "SELECT 1 FROM blacklist WHERE user_id = ?"
|
||
assert call_args[0][1] == (12345,)
|
||
|
||
# Проверяем логирование
|
||
blacklist_repository.logger.info.assert_called_once_with(
|
||
"Существует ли пользователь: user_id=12345 Итог: [(1,)]"
|
||
)
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_user_exists_false(self, blacklist_repository):
|
||
"""Тест проверки существования пользователя (пользователь не существует)"""
|
||
# Симулируем результат запроса - пользователь не найден
|
||
blacklist_repository._execute_query_with_result.return_value = []
|
||
|
||
result = await blacklist_repository.user_exists(12345)
|
||
|
||
# Проверяем, что возвращается False
|
||
assert result is False
|
||
|
||
# Проверяем логирование
|
||
blacklist_repository.logger.info.assert_called_once_with(
|
||
"Существует ли пользователь: user_id=12345 Итог: []"
|
||
)
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_get_user_success(self, blacklist_repository):
|
||
"""Тест успешного получения пользователя по ID"""
|
||
# Симулируем результат запроса
|
||
mock_row = (12345, "Нарушение правил", int(time.time()) + 86400, int(time.time()), 111)
|
||
blacklist_repository._execute_query_with_result.return_value = [mock_row]
|
||
|
||
result = await blacklist_repository.get_user(12345)
|
||
|
||
# Проверяем, что возвращается правильный объект
|
||
assert result is not None
|
||
assert result.user_id == 12345
|
||
assert result.message_for_user == "Нарушение правил"
|
||
assert result.date_to_unban == mock_row[2]
|
||
assert result.created_at == mock_row[3]
|
||
assert result.ban_author == mock_row[4]
|
||
|
||
# Проверяем, что метод вызван с правильными параметрами
|
||
blacklist_repository._execute_query_with_result.assert_called_once()
|
||
call_args = blacklist_repository._execute_query_with_result.call_args
|
||
|
||
assert "SELECT user_id, message_for_user, date_to_unban, created_at, ban_author" in call_args[0][0]
|
||
assert call_args[0][1] == (12345,)
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_get_user_not_found(self, blacklist_repository):
|
||
"""Тест получения пользователя по ID (пользователь не найден)"""
|
||
# Симулируем результат запроса - пользователь не найден
|
||
blacklist_repository._execute_query_with_result.return_value = []
|
||
|
||
result = await blacklist_repository.get_user(12345)
|
||
|
||
# Проверяем, что возвращается None
|
||
assert result is None
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_get_all_users_with_limits(self, blacklist_repository):
|
||
"""Тест получения пользователей с лимитами"""
|
||
# Симулируем результат запроса
|
||
mock_rows = [
|
||
(12345, "Нарушение правил", int(time.time()) + 86400, int(time.time())),
|
||
(67890, "Постоянный бан", None, int(time.time()) - 86400)
|
||
]
|
||
blacklist_repository._execute_query_with_result.return_value = mock_rows
|
||
|
||
result = await blacklist_repository.get_all_users(offset=0, limit=10)
|
||
|
||
# Проверяем, что возвращается правильный список
|
||
assert len(result) == 2
|
||
assert result[0].user_id == 12345
|
||
assert result[0].message_for_user == "Нарушение правил"
|
||
assert result[1].user_id == 67890
|
||
assert result[1].message_for_user == "Постоянный бан"
|
||
assert result[1].date_to_unban is None
|
||
|
||
# Проверяем, что метод вызван с правильными параметрами
|
||
blacklist_repository._execute_query_with_result.assert_called_once()
|
||
call_args = blacklist_repository._execute_query_with_result.call_args
|
||
|
||
assert call_args[0][0] == "SELECT user_id, message_for_user, date_to_unban, created_at FROM blacklist LIMIT ?, ?"
|
||
assert call_args[0][1] == (0, 10)
|
||
|
||
# Проверяем логирование
|
||
blacklist_repository.logger.info.assert_called_once_with(
|
||
"Получен список пользователей в черном списке (offset=0, limit=10): 2"
|
||
)
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_get_all_users_no_limit(self, blacklist_repository):
|
||
"""Тест получения всех пользователей без лимитов"""
|
||
# Симулируем результат запроса
|
||
mock_rows = [
|
||
(12345, "Нарушение правил", int(time.time()) + 86400, int(time.time())),
|
||
(67890, "Постоянный бан", None, int(time.time()) - 86400)
|
||
]
|
||
blacklist_repository._execute_query_with_result.return_value = mock_rows
|
||
|
||
result = await blacklist_repository.get_all_users_no_limit()
|
||
|
||
# Проверяем, что возвращается правильный список
|
||
assert len(result) == 2
|
||
|
||
# Проверяем, что метод вызван без лимитов
|
||
blacklist_repository._execute_query_with_result.assert_called_once()
|
||
call_args = blacklist_repository._execute_query_with_result.call_args
|
||
|
||
assert call_args[0][0] == "SELECT user_id, message_for_user, date_to_unban, created_at FROM blacklist"
|
||
# Проверяем, что параметры пустые (без лимитов)
|
||
assert len(call_args[0]) == 1 # Только SQL запрос, без параметров
|
||
|
||
# Проверяем логирование
|
||
blacklist_repository.logger.info.assert_called_once_with(
|
||
"Получен список всех пользователей в черном списке: 2"
|
||
)
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_get_users_for_unblock_today(self, blacklist_repository):
|
||
"""Тест получения пользователей для разблокировки сегодня"""
|
||
current_timestamp = int(time.time())
|
||
|
||
# Симулируем результат запроса - пользователи с истекшим сроком
|
||
mock_rows = [(12345,), (67890,)]
|
||
blacklist_repository._execute_query_with_result.return_value = mock_rows
|
||
|
||
result = await blacklist_repository.get_users_for_unblock_today(current_timestamp)
|
||
|
||
# Проверяем, что возвращается правильный словарь
|
||
assert len(result) == 2
|
||
assert 12345 in result
|
||
assert 67890 in result
|
||
assert result[12345] == 12345
|
||
assert result[67890] == 67890
|
||
|
||
# Проверяем, что метод вызван с правильными параметрами
|
||
blacklist_repository._execute_query_with_result.assert_called_once()
|
||
call_args = blacklist_repository._execute_query_with_result.call_args
|
||
|
||
assert call_args[0][0] == "SELECT user_id FROM blacklist WHERE date_to_unban IS NOT NULL AND date_to_unban <= ?"
|
||
assert call_args[0][1] == (current_timestamp,)
|
||
|
||
# Проверяем логирование
|
||
blacklist_repository.logger.info.assert_called_once_with(
|
||
"Получен список пользователей для разблокировки: {12345: 12345, 67890: 67890}"
|
||
)
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_get_users_for_unblock_today_empty(self, blacklist_repository):
|
||
"""Тест получения пользователей для разблокировки (пустой результат)"""
|
||
current_timestamp = int(time.time())
|
||
|
||
# Симулируем пустой результат запроса
|
||
blacklist_repository._execute_query_with_result.return_value = []
|
||
|
||
result = await blacklist_repository.get_users_for_unblock_today(current_timestamp)
|
||
|
||
# Проверяем, что возвращается пустой словарь
|
||
assert result == {}
|
||
|
||
# Проверяем логирование
|
||
blacklist_repository.logger.info.assert_called_once_with(
|
||
"Получен список пользователей для разблокировки: {}"
|
||
)
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_get_count(self, blacklist_repository):
|
||
"""Тест получения количества пользователей в черном списке"""
|
||
# Симулируем результат запроса
|
||
blacklist_repository._execute_query_with_result.return_value = [(5,)]
|
||
|
||
result = await blacklist_repository.get_count()
|
||
|
||
# Проверяем, что возвращается правильное количество
|
||
assert result == 5
|
||
|
||
# Проверяем, что метод вызван с правильными параметрами
|
||
blacklist_repository._execute_query_with_result.assert_called_once()
|
||
call_args = blacklist_repository._execute_query_with_result.call_args
|
||
|
||
assert call_args[0][0] == "SELECT COUNT(*) FROM blacklist"
|
||
# Проверяем, что параметры пустые
|
||
assert len(call_args[0]) == 1 # Только SQL запрос, без параметров
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_get_count_zero(self, blacklist_repository):
|
||
"""Тест получения количества пользователей (0 пользователей)"""
|
||
# Симулируем пустой результат запроса
|
||
blacklist_repository._execute_query_with_result.return_value = []
|
||
|
||
result = await blacklist_repository.get_count()
|
||
|
||
# Проверяем, что возвращается 0
|
||
assert result == 0
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_get_count_none_result(self, blacklist_repository):
|
||
"""Тест получения количества пользователей (None результат)"""
|
||
# Симулируем None результат запроса
|
||
blacklist_repository._execute_query_with_result.return_value = None
|
||
|
||
result = await blacklist_repository.get_count()
|
||
|
||
# Проверяем, что возвращается 0
|
||
assert result == 0
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_error_handling_in_get_user(self, blacklist_repository):
|
||
"""Тест обработки ошибок при получении пользователя"""
|
||
# Симулируем ошибку базы данных
|
||
blacklist_repository._execute_query_with_result.side_effect = Exception("Database connection failed")
|
||
|
||
# Проверяем, что исключение пробрасывается
|
||
with pytest.raises(Exception) as exc_info:
|
||
await blacklist_repository.get_user(12345)
|
||
|
||
assert "Database connection failed" in str(exc_info.value)
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_error_handling_in_get_all_users(self, blacklist_repository):
|
||
"""Тест обработки ошибок при получении всех пользователей"""
|
||
# Симулируем ошибку базы данных
|
||
blacklist_repository._execute_query_with_result.side_effect = Exception("Database connection failed")
|
||
|
||
# Проверяем, что исключение пробрасывается
|
||
with pytest.raises(Exception) as exc_info:
|
||
await blacklist_repository.get_all_users()
|
||
|
||
assert "Database connection failed" in str(exc_info.value)
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_error_handling_in_get_count(self, blacklist_repository):
|
||
"""Тест обработки ошибок при получении количества"""
|
||
# Симулируем ошибку базы данных
|
||
blacklist_repository._execute_query_with_result.side_effect = Exception("Database connection failed")
|
||
|
||
# Проверяем, что исключение пробрасывается
|
||
with pytest.raises(Exception) as exc_info:
|
||
await blacklist_repository.get_count()
|
||
|
||
assert "Database connection failed" in str(exc_info.value)
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_error_handling_in_get_users_for_unblock_today(self, blacklist_repository):
|
||
"""Тест обработки ошибок при получении пользователей для разблокировки"""
|
||
# Симулируем ошибку базы данных
|
||
blacklist_repository._execute_query_with_result.side_effect = Exception("Database connection failed")
|
||
|
||
# Проверяем, что исключение пробрасывается
|
||
with pytest.raises(Exception) as exc_info:
|
||
await blacklist_repository.get_users_for_unblock_today(int(time.time()))
|
||
|
||
assert "Database connection failed" in str(exc_info.value)
|
||
|
||
# TODO: 20-й тест - test_integration_workflow
|
||
# Этот тест должен проверять полный рабочий процесс:
|
||
# 1. Добавление пользователя в черный список
|
||
# 2. Проверка существования пользователя
|
||
# 3. Получение информации о пользователе
|
||
# 4. Получение общего количества пользователей
|
||
# 5. Удаление пользователя из черного списка
|
||
# 6. Проверка, что пользователь больше не существует
|
||
#
|
||
# Проблема: тест падает из-за сложности мокирования возвращаемых значений
|
||
# при создании объектов BlacklistUser из результатов запросов к БД.
|
||
# Требует более сложной настройки моков для корректной работы.
|