Files
telegram-helper-bot/tests/test_post_repository.py
2026-02-02 00:13:33 +03:00

777 lines
32 KiB
Python
Raw Permalink 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.
import asyncio
from datetime import datetime
from unittest.mock import AsyncMock, MagicMock
import pytest
from database.models import MessageContentLink, PostContent, TelegramPost
from database.repositories.post_repository import PostRepository
class TestPostRepository:
"""Тесты для PostRepository."""
@pytest.fixture
def mock_db_path(self):
"""Фикстура для пути к тестовой БД."""
return ":memory:"
@pytest.fixture
def post_repository(self, mock_db_path):
"""Фикстура для PostRepository."""
return PostRepository(mock_db_path)
@pytest.fixture
def sample_post(self):
"""Фикстура для тестового поста."""
return TelegramPost(
message_id=12345,
text="Тестовый пост",
author_id=67890,
helper_text_message_id=None,
created_at=int(datetime.now().timestamp()),
)
@pytest.fixture
def sample_post_no_date(self):
"""Фикстура для тестового поста без даты."""
return TelegramPost(
message_id=12346,
text="Тестовый пост без даты",
author_id=67890,
helper_text_message_id=None,
created_at=None,
status="suggest",
)
@pytest.fixture
def sample_post_content(self):
"""Фикстура для тестового контента поста."""
return PostContent(
message_id=12345, content_name="/path/to/file.jpg", content_type="photo"
)
@pytest.fixture
def sample_message_link(self):
"""Фикстура для тестовой связи сообщения с контентом."""
return MessageContentLink(post_id=12345, message_id=67890)
@pytest.mark.asyncio
async def test_create_tables(self, post_repository):
"""Тест создания таблиц."""
# Мокаем _execute_query и _execute_query_with_result
post_repository._execute_query = AsyncMock()
post_repository._execute_query_with_result = AsyncMock(
return_value=[]
) # Для проверки столбца
await post_repository.create_tables()
# Проверяем, что create_tables вызвался минимум 3 раза (для каждой таблицы)
# Может быть больше из-за ALTER TABLE и индексов
assert post_repository._execute_query.call_count >= 3
# Проверяем, что все нужные таблицы созданы (порядок может быть разным из-за ALTER TABLE)
calls = post_repository._execute_query.call_args_list
all_queries = [call[0][0] for call in calls]
# Проверяем создание таблицы постов
post_table_queries = [
q
for q in all_queries
if "CREATE TABLE IF NOT EXISTS post_from_telegram_suggest" in q
]
assert len(post_table_queries) > 0
assert "message_id INTEGER NOT NULL PRIMARY KEY" in post_table_queries[0]
assert "created_at INTEGER NOT NULL" in post_table_queries[0]
assert "status TEXT NOT NULL DEFAULT 'suggest'" in post_table_queries[0]
assert "is_anonymous INTEGER" in post_table_queries[0]
assert (
"FOREIGN KEY (author_id) REFERENCES our_users (user_id) ON DELETE CASCADE"
in post_table_queries[0]
)
# Проверяем создание таблицы контента
content_table_queries = [
q
for q in all_queries
if "CREATE TABLE IF NOT EXISTS content_post_from_telegram" in q
]
assert len(content_table_queries) > 0
assert "PRIMARY KEY (message_id, content_name)" in content_table_queries[0]
# Проверяем создание таблицы связей
link_table_queries = [
q
for q in all_queries
if "CREATE TABLE IF NOT EXISTS message_link_to_content" in q
]
assert len(link_table_queries) > 0
assert "PRIMARY KEY (post_id, message_id)" in link_table_queries[0]
@pytest.mark.asyncio
async def test_add_post_with_date(self, post_repository, sample_post):
"""Тест добавления поста с датой."""
# Мокаем _execute_query
post_repository._execute_query = AsyncMock()
await post_repository.add_post(sample_post)
post_repository._execute_query.assert_called_once()
call_args = post_repository._execute_query.call_args
query = call_args[0][0]
params = call_args[0][1]
assert "INSERT OR IGNORE INTO post_from_telegram_suggest" in query
assert "status" in query
assert "is_anonymous" in query
assert "VALUES (?, ?, ?, ?, ?, ?)" in query
# Проверяем параметры: message_id, text, author_id, created_at, status, is_anonymous
assert params[0] == sample_post.message_id
assert params[1] == sample_post.text
assert params[2] == sample_post.author_id
assert params[3] == sample_post.created_at
assert params[4] == sample_post.status
# is_anonymous преобразуется в int (None -> None, True -> 1, False -> 0)
expected_is_anonymous = (
None
if sample_post.is_anonymous is None
else (1 if sample_post.is_anonymous else 0)
)
assert params[5] == expected_is_anonymous
@pytest.mark.asyncio
async def test_add_post_without_date(self, post_repository, sample_post_no_date):
"""Тест добавления поста без даты (должна генерироваться автоматически)."""
# Мокаем _execute_query
post_repository._execute_query = AsyncMock()
await post_repository.add_post(sample_post_no_date)
# Проверяем, что дата была установлена
assert sample_post_no_date.created_at is not None
assert isinstance(sample_post_no_date.created_at, int)
assert sample_post_no_date.created_at > 0
post_repository._execute_query.assert_called_once()
call_args = post_repository._execute_query.call_args
params = call_args[0][1]
assert params[3] == sample_post_no_date.created_at # created_at
assert params[4] == sample_post_no_date.status # status (default suggest)
# Проверяем is_anonymous (должен быть в параметрах)
assert len(params) == 6 # Всего 6 параметров включая is_anonymous
@pytest.mark.asyncio
async def test_add_post_logs_correctly(self, post_repository, sample_post):
"""Тест логирования при добавлении поста."""
# Мокаем _execute_query и logger
post_repository._execute_query = AsyncMock()
post_repository.logger = MagicMock()
await post_repository.add_post(sample_post)
# Проверяем, что логирование вызвано с новым форматом сообщения
post_repository.logger.info.assert_called_once()
log_call = post_repository.logger.info.call_args[0][0]
assert f"message_id={sample_post.message_id}" in log_call
assert "Пост добавлен" in log_call or "уже существует" in log_call
@pytest.mark.asyncio
async def test_update_helper_message(self, post_repository):
"""Тест обновления helper сообщения."""
# Мокаем _execute_query
post_repository._execute_query = AsyncMock()
message_id = 12345
helper_message_id = 67890
await post_repository.update_helper_message(message_id, helper_message_id)
post_repository._execute_query.assert_called_once()
call_args = post_repository._execute_query.call_args
query = call_args[0][0]
params = call_args[0][1]
assert (
"UPDATE post_from_telegram_suggest SET helper_text_message_id = ? WHERE message_id = ?"
in query
)
assert params == (helper_message_id, message_id)
@pytest.mark.asyncio
async def test_update_status_by_message_id(self, post_repository):
"""Тест обновления статуса поста по message_id."""
# Создаем таблицы перед тестом
post_repository._execute_query = AsyncMock()
post_repository._execute_query_with_result = AsyncMock(return_value=[])
post_repository._get_connection = AsyncMock()
mock_conn = AsyncMock()
mock_cur = AsyncMock()
mock_cur.fetchone = AsyncMock(return_value=(1,)) # 1 строка обновлена
mock_conn.execute = AsyncMock(return_value=mock_cur)
post_repository._get_connection.return_value = mock_conn
post_repository.logger = MagicMock()
# Создаем таблицы
await post_repository.create_tables()
post_repository._execute_query.reset_mock()
post_repository._execute_query_with_result.reset_mock()
post_repository.logger.info.reset_mock() # Сбрасываем счетчик логирования
message_id = 12345
status = "approved"
await post_repository.update_status_by_message_id(message_id, status)
# Проверяем, что conn.execute был вызван с правильными параметрами
assert mock_conn.execute.call_count >= 1
update_call = mock_conn.execute.call_args_list[0]
query = update_call[0][0]
params = update_call[0][1]
assert "UPDATE post_from_telegram_suggest" in query
assert "SET status = ? WHERE message_id = ?" in query
assert params == (status, message_id)
# Проверяем, что после создания таблиц было вызвано логирование обновления статуса
post_repository.logger.info.assert_called()
log_calls = [str(call) for call in post_repository.logger.info.call_args_list]
assert any(
"Статус поста message_id=12345 обновлён на approved" in str(call)
for call in post_repository.logger.info.call_args_list
)
@pytest.mark.asyncio
async def test_update_status_for_media_group_by_helper_id(self, post_repository):
"""Тест обновления статуса медиагруппы по helper_message_id."""
# Создаем таблицы перед тестом
post_repository._execute_query = AsyncMock()
post_repository._execute_query_with_result = AsyncMock(return_value=[])
post_repository._get_connection = AsyncMock()
mock_conn = AsyncMock()
mock_cur = AsyncMock()
mock_cur.fetchone = AsyncMock(return_value=(1,)) # 1 строка обновлена
mock_conn.execute = AsyncMock(return_value=mock_cur)
post_repository._get_connection.return_value = mock_conn
post_repository.logger = MagicMock()
# Создаем таблицы
await post_repository.create_tables()
post_repository._execute_query.reset_mock()
post_repository._execute_query_with_result.reset_mock()
post_repository.logger.info.reset_mock() # Сбрасываем счетчик логирования
helper_message_id = 99999
status = "declined"
await post_repository.update_status_for_media_group_by_helper_id(
helper_message_id, status
)
# Проверяем, что conn.execute был вызван с правильными параметрами
assert mock_conn.execute.call_count >= 1
update_call = mock_conn.execute.call_args_list[0]
query = update_call[0][0]
params = update_call[0][1]
assert "UPDATE post_from_telegram_suggest" in query
assert "SET status = ?" in query
assert "message_id = ? OR helper_text_message_id = ?" in query
assert params == (status, helper_message_id, helper_message_id)
# Проверяем, что после создания таблиц было вызвано логирование обновления статуса
post_repository.logger.info.assert_called()
assert any(
"Статус медиагруппы helper_message_id=99999 обновлён на declined"
in str(call)
for call in post_repository.logger.info.call_args_list
)
@pytest.mark.asyncio
async def test_add_post_content_success(self, post_repository):
"""Тест успешного добавления контента поста."""
# Мокаем _execute_query
post_repository._execute_query = AsyncMock()
post_repository.logger = MagicMock()
post_id = 12345
message_id = 67890
content_name = "/path/to/file.jpg"
content_type = "photo"
result = await post_repository.add_post_content(
post_id, message_id, content_name, content_type
)
# Проверяем, что результат True
assert result is True
# Проверяем, что _execute_query вызвался 2 раза (для связи и контента)
assert post_repository._execute_query.call_count == 2
# Проверяем вызов для связи
link_call = post_repository._execute_query.call_args_list[0]
link_query = link_call[0][0]
link_params = link_call[0][1]
assert "INSERT OR IGNORE INTO message_link_to_content" in link_query
assert link_params == (post_id, message_id)
# Проверяем вызов для контента
content_call = post_repository._execute_query.call_args_list[1]
content_query = content_call[0][0]
content_params = content_call[0][1]
assert "INSERT OR IGNORE INTO content_post_from_telegram" in content_query
assert content_params == (message_id, content_name, content_type)
# Проверяем логирование
post_repository.logger.info.assert_called_once_with(
f"Контент поста добавлен: post_id={post_id}, message_id={message_id}"
)
@pytest.mark.asyncio
async def test_add_post_content_exception(self, post_repository):
"""Тест обработки исключения при добавлении контента поста."""
# Мокаем _execute_query чтобы вызвать исключение
post_repository._execute_query = AsyncMock(
side_effect=Exception("Database error")
)
post_repository.logger = MagicMock()
post_id = 12345
message_id = 67890
content_name = "/path/to/file.jpg"
content_type = "photo"
result = await post_repository.add_post_content(
post_id, message_id, content_name, content_type
)
# Проверяем, что результат False
assert result is False
# Проверяем логирование ошибки
post_repository.logger.error.assert_called_once()
error_call = post_repository.logger.error.call_args[0][0]
assert "Ошибка при добавлении контента поста:" in error_call
@pytest.mark.asyncio
async def test_get_post_content_by_helper_id(self, post_repository):
"""Тест получения контента поста по helper ID."""
# Мокаем _execute_query_with_result
mock_result = [
("/path/to/photo1.jpg", "photo"),
("/path/to/video1.mp4", "video"),
("/path/to/photo2.jpg", "photo"),
]
post_repository._execute_query_with_result = AsyncMock(return_value=mock_result)
post_repository.logger = MagicMock()
helper_message_id = 67890
result = await post_repository.get_post_content_by_helper_id(helper_message_id)
# Проверяем результат
assert result == mock_result
# Проверяем вызов _execute_query_with_result
post_repository._execute_query_with_result.assert_called_once()
call_args = post_repository._execute_query_with_result.call_args
query = call_args[0][0]
params = call_args[0][1]
assert "SELECT cpft.content_name, cpft.content_type" in query
assert "WHERE pft.helper_text_message_id = ?" in query
assert params == (helper_message_id,)
# Проверяем логирование
post_repository.logger.info.assert_called_once_with(
f"Получен контент поста: {len(mock_result)} элементов"
)
@pytest.mark.asyncio
async def test_get_post_text_by_helper_id_found(self, post_repository):
"""Тест получения текста поста по helper ID (пост найден)."""
# Мокаем _execute_query_with_result
mock_result = [("Тестовый текст поста",)]
post_repository._execute_query_with_result = AsyncMock(return_value=mock_result)
post_repository.logger = MagicMock()
helper_message_id = 67890
result = await post_repository.get_post_text_by_helper_id(helper_message_id)
# Проверяем результат
assert result == "Тестовый текст поста"
# Проверяем вызов _execute_query_with_result
post_repository._execute_query_with_result.assert_called_once()
call_args = post_repository._execute_query_with_result.call_args
query = call_args[0][0]
params = call_args[0][1]
assert (
"SELECT text FROM post_from_telegram_suggest WHERE helper_text_message_id = ?"
in query
)
assert params == (helper_message_id,)
# Проверяем логирование
post_repository.logger.info.assert_called_once_with(
f"Получен текст поста для helper_message_id={helper_message_id}"
)
@pytest.mark.asyncio
async def test_get_post_text_by_helper_id_not_found(self, post_repository):
"""Тест получения текста поста по helper ID (пост не найден)."""
# Мокаем _execute_query_with_result
mock_result = []
post_repository._execute_query_with_result = AsyncMock(return_value=mock_result)
post_repository.logger = MagicMock()
helper_message_id = 67890
result = await post_repository.get_post_text_by_helper_id(helper_message_id)
# Проверяем результат
assert result is None
# Проверяем, что logger.info не вызывался
post_repository.logger.info.assert_not_called()
@pytest.mark.asyncio
async def test_get_post_ids_by_helper_id(self, post_repository):
"""Тест получения ID сообщений по helper ID."""
# Мокаем _execute_query_with_result
mock_result = [(12345,), (67890,), (11111,)]
post_repository._execute_query_with_result = AsyncMock(return_value=mock_result)
post_repository.logger = MagicMock()
helper_message_id = 67890
result = await post_repository.get_post_ids_by_helper_id(helper_message_id)
# Проверяем результат
assert result == [12345, 67890, 11111]
# Проверяем вызов _execute_query_with_result
post_repository._execute_query_with_result.assert_called_once()
call_args = post_repository._execute_query_with_result.call_args
query = call_args[0][0]
params = call_args[0][1]
assert "SELECT mltc.message_id" in query
assert "WHERE pft.helper_text_message_id = ?" in query
assert params == (helper_message_id,)
# Проверяем логирование
post_repository.logger.info.assert_called_once_with(
f"Получены ID сообщений: {len(mock_result)} элементов"
)
@pytest.mark.asyncio
async def test_get_author_id_by_message_id_found(self, post_repository):
"""Тест получения ID автора по message ID (автор найден)."""
# Мокаем _execute_query_with_result
mock_result = [(67890,)]
post_repository._execute_query_with_result = AsyncMock(return_value=mock_result)
post_repository.logger = MagicMock()
message_id = 12345
result = await post_repository.get_author_id_by_message_id(message_id)
# Проверяем результат
assert result == 67890
# Проверяем вызов _execute_query_with_result
post_repository._execute_query_with_result.assert_called_once()
call_args = post_repository._execute_query_with_result.call_args
query = call_args[0][0]
params = call_args[0][1]
assert (
"SELECT author_id FROM post_from_telegram_suggest WHERE message_id = ?"
in query
)
assert params == (message_id,)
# Проверяем логирование
post_repository.logger.info.assert_called_once_with(
f"Получен author_id: {67890} для message_id={message_id}"
)
@pytest.mark.asyncio
async def test_get_author_id_by_message_id_not_found(self, post_repository):
"""Тест получения ID автора по message ID (автор не найден)."""
# Мокаем _execute_query_with_result
mock_result = []
post_repository._execute_query_with_result = AsyncMock(return_value=mock_result)
post_repository.logger = MagicMock()
message_id = 12345
result = await post_repository.get_author_id_by_message_id(message_id)
# Проверяем результат
assert result is None
# Проверяем, что logger.info не вызывался
post_repository.logger.info.assert_not_called()
@pytest.mark.asyncio
async def test_get_author_id_by_helper_message_id_found(self, post_repository):
"""Тест получения ID автора по helper message ID (автор найден)."""
# Мокаем _execute_query_with_result
mock_result = [(67890,)]
post_repository._execute_query_with_result = AsyncMock(return_value=mock_result)
post_repository.logger = MagicMock()
helper_message_id = 12345
result = await post_repository.get_author_id_by_helper_message_id(
helper_message_id
)
# Проверяем результат
assert result == 67890
# Проверяем вызов _execute_query_with_result
post_repository._execute_query_with_result.assert_called_once()
call_args = post_repository._execute_query_with_result.call_args
query = call_args[0][0]
params = call_args[0][1]
assert (
"SELECT author_id FROM post_from_telegram_suggest WHERE helper_text_message_id = ?"
in query
)
assert params == (helper_message_id,)
# Проверяем логирование
post_repository.logger.info.assert_called_once_with(
f"Получен author_id: {67890} для helper_message_id={helper_message_id}"
)
@pytest.mark.asyncio
async def test_get_author_id_by_helper_message_id_not_found(self, post_repository):
"""Тест получения ID автора по helper message ID (автор не найден)."""
# Мокаем _execute_query_with_result
mock_result = []
post_repository._execute_query_with_result = AsyncMock(return_value=mock_result)
post_repository.logger = MagicMock()
helper_message_id = 12345
result = await post_repository.get_author_id_by_helper_message_id(
helper_message_id
)
# Проверяем результат
assert result is None
# Проверяем, что logger.info не вызывался
post_repository.logger.info.assert_not_called()
@pytest.mark.asyncio
async def test_get_post_text_and_anonymity_by_message_id_found(
self, post_repository
):
"""Тест получения текста и is_anonymous по message_id (пост найден)."""
# Мокаем _execute_query_with_result
mock_result = [("Тестовый текст", 1)] # is_anonymous = 1 (True)
post_repository._execute_query_with_result = AsyncMock(return_value=mock_result)
post_repository.logger = MagicMock()
message_id = 12345
result = await post_repository.get_post_text_and_anonymity_by_message_id(
message_id
)
# Проверяем результат
text, is_anonymous = result
assert text == "Тестовый текст"
assert is_anonymous is True
# Проверяем вызов _execute_query_with_result
post_repository._execute_query_with_result.assert_called_once()
call_args = post_repository._execute_query_with_result.call_args
query = call_args[0][0]
params = call_args[0][1]
assert (
"SELECT text, is_anonymous FROM post_from_telegram_suggest WHERE message_id = ?"
in query
)
assert params == (message_id,)
@pytest.mark.asyncio
async def test_get_post_text_and_anonymity_by_message_id_with_false(
self, post_repository
):
"""Тест получения текста и is_anonymous по message_id (is_anonymous = False)."""
# Мокаем _execute_query_with_result
mock_result = [("Тестовый текст", 0)] # is_anonymous = 0 (False)
post_repository._execute_query_with_result = AsyncMock(return_value=mock_result)
message_id = 12345
result = await post_repository.get_post_text_and_anonymity_by_message_id(
message_id
)
# Проверяем результат
text, is_anonymous = result
assert text == "Тестовый текст"
assert is_anonymous is False
@pytest.mark.asyncio
async def test_get_post_text_and_anonymity_by_message_id_with_null(
self, post_repository
):
"""Тест получения текста и is_anonymous по message_id (is_anonymous = NULL)."""
# Мокаем _execute_query_with_result
mock_result = [("Тестовый текст", None)] # is_anonymous = NULL
post_repository._execute_query_with_result = AsyncMock(return_value=mock_result)
message_id = 12345
result = await post_repository.get_post_text_and_anonymity_by_message_id(
message_id
)
# Проверяем результат
text, is_anonymous = result
assert text == "Тестовый текст"
assert is_anonymous is None
@pytest.mark.asyncio
async def test_get_post_text_and_anonymity_by_message_id_not_found(
self, post_repository
):
"""Тест получения текста и is_anonymous по message_id (пост не найден)."""
# Мокаем _execute_query_with_result
mock_result = []
post_repository._execute_query_with_result = AsyncMock(return_value=mock_result)
message_id = 12345
result = await post_repository.get_post_text_and_anonymity_by_message_id(
message_id
)
# Проверяем результат
text, is_anonymous = result
assert text is None
assert is_anonymous is None
@pytest.mark.asyncio
async def test_get_post_text_and_anonymity_by_helper_id_found(
self, post_repository
):
"""Тест получения текста и is_anonymous по helper_message_id (пост найден)."""
# Мокаем _execute_query_with_result
mock_result = [("Тестовый текст", 1)] # is_anonymous = 1 (True)
post_repository._execute_query_with_result = AsyncMock(return_value=mock_result)
post_repository.logger = MagicMock()
helper_message_id = 67890
result = await post_repository.get_post_text_and_anonymity_by_helper_id(
helper_message_id
)
# Проверяем результат
text, is_anonymous = result
assert text == "Тестовый текст"
assert is_anonymous is True
# Проверяем вызов _execute_query_with_result
post_repository._execute_query_with_result.assert_called_once()
call_args = post_repository._execute_query_with_result.call_args
query = call_args[0][0]
params = call_args[0][1]
assert (
"SELECT text, is_anonymous FROM post_from_telegram_suggest WHERE helper_text_message_id = ?"
in query
)
assert params == (helper_message_id,)
@pytest.mark.asyncio
async def test_add_post_with_is_anonymous_true(self, post_repository):
"""Тест добавления поста с is_anonymous=True."""
post = TelegramPost(
message_id=12345,
text="Тестовый пост анон",
author_id=67890,
created_at=int(datetime.now().timestamp()),
is_anonymous=True,
)
post_repository._execute_query = AsyncMock()
await post_repository.add_post(post)
call_args = post_repository._execute_query.call_args
params = call_args[0][1]
# Проверяем, что is_anonymous преобразован в 1
assert params[5] == 1
@pytest.mark.asyncio
async def test_add_post_with_is_anonymous_false(self, post_repository):
"""Тест добавления поста с is_anonymous=False."""
post = TelegramPost(
message_id=12345,
text="Тестовый пост неанон",
author_id=67890,
created_at=int(datetime.now().timestamp()),
is_anonymous=False,
)
post_repository._execute_query = AsyncMock()
await post_repository.add_post(post)
call_args = post_repository._execute_query.call_args
params = call_args[0][1]
# Проверяем, что is_anonymous преобразован в 0
assert params[5] == 0
@pytest.mark.asyncio
async def test_add_post_with_is_anonymous_none(self, post_repository):
"""Тест добавления поста с is_anonymous=None."""
post = TelegramPost(
message_id=12345,
text="Тестовый пост",
author_id=67890,
created_at=int(datetime.now().timestamp()),
is_anonymous=None,
)
post_repository._execute_query = AsyncMock()
await post_repository.add_post(post)
call_args = post_repository._execute_query.call_args
params = call_args[0][1]
# Проверяем, что is_anonymous остался None
assert params[5] is None
@pytest.mark.asyncio
async def test_create_tables_logs_success(self, post_repository):
"""Тест логирования успешного создания таблиц."""
# Мокаем _execute_query, _execute_query_with_result и logger
post_repository._execute_query = AsyncMock()
post_repository._execute_query_with_result = AsyncMock(
return_value=[]
) # Для проверки столбца
post_repository.logger = MagicMock()
await post_repository.create_tables()
# Проверяем, что финальное сообщение о создании таблиц было вызвано
post_repository.logger.info.assert_any_call("Таблицы для постов созданы")