fix quality code

This commit is contained in:
2026-02-01 23:03:23 +03:00
parent 731e68a597
commit f8962225ee
106 changed files with 8456 additions and 5810 deletions

View File

@@ -3,13 +3,14 @@ from datetime import datetime
from unittest.mock import AsyncMock, MagicMock, Mock, patch
import pytest
from database.models import AudioListenRecord, AudioMessage, AudioModerate
from database.repositories.audio_repository import AudioRepository
class TestAudioRepository:
"""Тесты для AudioRepository"""
@pytest.fixture
def mock_db_connection(self):
"""Мок для DatabaseConnection"""
@@ -18,18 +19,20 @@ class TestAudioRepository:
mock_connection._execute_query_with_result = AsyncMock()
mock_connection.logger = Mock()
return mock_connection
@pytest.fixture
def audio_repository(self, mock_db_connection):
"""Экземпляр AudioRepository для тестов"""
# Патчим наследование от DatabaseConnection
with patch.object(AudioRepository, '__init__', return_value=None):
with patch.object(AudioRepository, "__init__", return_value=None):
repo = AudioRepository()
repo._execute_query = mock_db_connection._execute_query
repo._execute_query_with_result = mock_db_connection._execute_query_with_result
repo._execute_query_with_result = (
mock_db_connection._execute_query_with_result
)
repo.logger = mock_db_connection.logger
return repo
@pytest.fixture
def sample_audio_message(self):
"""Тестовое аудио сообщение"""
@@ -38,45 +41,49 @@ class TestAudioRepository:
author_id=12345,
date_added="2025-01-15 14:30:00",
file_id="test_file_id",
listen_count=0
listen_count=0,
)
@pytest.fixture
def sample_datetime(self):
"""Тестовая дата"""
return datetime(2025, 1, 15, 14, 30, 0)
@pytest.fixture
def sample_timestamp(self):
"""Тестовый UNIX timestamp"""
return int(time.mktime(datetime(2025, 1, 15, 14, 30, 0).timetuple()))
@pytest.mark.asyncio
async def test_enable_foreign_keys(self, audio_repository):
"""Тест включения внешних ключей"""
await audio_repository.enable_foreign_keys()
audio_repository._execute_query.assert_called_once_with("PRAGMA foreign_keys = ON;")
audio_repository._execute_query.assert_called_once_with(
"PRAGMA foreign_keys = ON;"
)
@pytest.mark.asyncio
async def test_create_tables(self, audio_repository):
"""Тест создания таблиц"""
await audio_repository.create_tables()
# Проверяем, что все три таблицы созданы
assert audio_repository._execute_query.call_count == 3
# Проверяем вызовы для каждой таблицы
calls = audio_repository._execute_query.call_args_list
assert any("audio_message_reference" in str(call) for call in calls)
assert any("user_audio_listens" in str(call) for call in calls)
assert any("audio_moderate" in str(call) for call in calls)
@pytest.mark.asyncio
async def test_add_audio_record_with_string_date(self, audio_repository, sample_audio_message):
async def test_add_audio_record_with_string_date(
self, audio_repository, sample_audio_message
):
"""Тест добавления аудио записи со строковой датой"""
await audio_repository.add_audio_record(sample_audio_message)
# Проверяем, что метод вызван с правильными параметрами
audio_repository._execute_query.assert_called_once()
call_args = audio_repository._execute_query.call_args
@@ -88,7 +95,7 @@ class TestAudioRepository:
assert call_args[0][1][0] == "test_audio_123.ogg"
assert call_args[0][1][1] == 12345
assert isinstance(call_args[0][1][2], int) # timestamp
@pytest.mark.asyncio
async def test_add_audio_record_with_datetime_date(self, audio_repository):
"""Тест добавления аудио записи с datetime датой"""
@@ -97,15 +104,15 @@ class TestAudioRepository:
author_id=67890,
date_added=datetime(2025, 1, 20, 10, 15, 0),
file_id="test_file_id_2",
listen_count=0
listen_count=0,
)
await audio_repository.add_audio_record(audio_msg)
# Проверяем, что date_added преобразован в timestamp
call_args = audio_repository._execute_query.call_args
assert isinstance(call_args[0][1][2], int) # timestamp
@pytest.mark.asyncio
async def test_add_audio_record_with_timestamp_date(self, audio_repository):
"""Тест добавления аудио записи с timestamp датой"""
@@ -115,268 +122,293 @@ class TestAudioRepository:
author_id=11111,
date_added=timestamp,
file_id="test_file_id_3",
listen_count=0
listen_count=0,
)
await audio_repository.add_audio_record(audio_msg)
# Проверяем, что date_added остался timestamp
call_args = audio_repository._execute_query.call_args
assert call_args[0][1][2] == timestamp
@pytest.mark.asyncio
async def test_add_audio_record_simple_with_string_date(self, audio_repository):
"""Тест упрощенного добавления аудио записи со строковой датой"""
await audio_repository.add_audio_record_simple("test_audio.ogg", 12345, "2025-01-15 14:30:00")
await audio_repository.add_audio_record_simple(
"test_audio.ogg", 12345, "2025-01-15 14:30:00"
)
# Проверяем, что метод вызван
audio_repository._execute_query.assert_called_once()
call_args = audio_repository._execute_query.call_args
assert call_args[0][1][0] == "test_audio.ogg" # file_name
assert call_args[0][1][1] == 12345 # user_id
assert isinstance(call_args[0][1][2], int) # timestamp
@pytest.mark.asyncio
async def test_add_audio_record_simple_with_datetime_date(self, audio_repository, sample_datetime):
async def test_add_audio_record_simple_with_datetime_date(
self, audio_repository, sample_datetime
):
"""Тест упрощенного добавления аудио записи с datetime датой"""
await audio_repository.add_audio_record_simple("test_audio.ogg", 12345, sample_datetime)
await audio_repository.add_audio_record_simple(
"test_audio.ogg", 12345, sample_datetime
)
# Проверяем, что date_added преобразован в timestamp
call_args = audio_repository._execute_query.call_args
assert isinstance(call_args[0][1][2], int) # timestamp
@pytest.mark.asyncio
async def test_get_last_date_audio(self, audio_repository):
"""Тест получения даты последнего аудио"""
expected_timestamp = 1642248600 # 2022-01-17 10:30:00
audio_repository._execute_query_with_result.return_value = [(expected_timestamp,)]
audio_repository._execute_query_with_result.return_value = [
(expected_timestamp,)
]
result = await audio_repository.get_last_date_audio()
assert result == expected_timestamp
audio_repository._execute_query_with_result.assert_called_once_with(
"SELECT date_added FROM audio_message_reference ORDER BY date_added DESC LIMIT 1"
)
@pytest.mark.asyncio
async def test_get_last_date_audio_no_records(self, audio_repository):
"""Тест получения даты последнего аудио когда записей нет"""
audio_repository._execute_query_with_result.return_value = []
result = await audio_repository.get_last_date_audio()
assert result is None
@pytest.mark.asyncio
async def test_get_user_audio_records_count(self, audio_repository):
"""Тест получения количества аудио записей пользователя"""
audio_repository._execute_query_with_result.return_value = [(5,)]
result = await audio_repository.get_user_audio_records_count(12345)
assert result == 5
audio_repository._execute_query_with_result.assert_called_once_with(
"SELECT COUNT(*) FROM audio_message_reference WHERE author_id = ?", (12345,)
)
@pytest.mark.asyncio
async def test_get_path_for_audio_record(self, audio_repository):
"""Тест получения пути к аудио записи пользователя"""
audio_repository._execute_query_with_result.return_value = [("test_audio.ogg",)]
result = await audio_repository.get_path_for_audio_record(12345)
assert result == "test_audio.ogg"
audio_repository._execute_query_with_result.assert_called_once_with(
"""
SELECT file_name FROM audio_message_reference
WHERE author_id = ? ORDER BY date_added DESC LIMIT 1
""", (12345,)
""",
(12345,),
)
@pytest.mark.asyncio
async def test_get_path_for_audio_record_no_records(self, audio_repository):
"""Тест получения пути к аудио записи когда записей нет"""
audio_repository._execute_query_with_result.return_value = []
result = await audio_repository.get_path_for_audio_record(12345)
assert result is None
@pytest.mark.asyncio
async def test_check_listen_audio(self, audio_repository):
"""Тест проверки непрослушанных аудио"""
# Мокаем результаты запросов
audio_repository._execute_query_with_result.side_effect = [
[("audio1.ogg",), ("audio2.ogg",)], # прослушанные
[("audio1.ogg",), ("audio2.ogg",), ("audio3.ogg",)] # все аудио
[("audio1.ogg",), ("audio2.ogg",), ("audio3.ogg",)], # все аудио
]
result = await audio_repository.check_listen_audio(12345)
# Должно вернуться только непрослушанные (audio3.ogg)
assert result == ["audio3.ogg"]
assert audio_repository._execute_query_with_result.call_count == 2
@pytest.mark.asyncio
async def test_mark_listened_audio(self, audio_repository):
"""Тест отметки аудио как прослушанного"""
await audio_repository.mark_listened_audio("test_audio.ogg", 12345)
audio_repository._execute_query.assert_called_once_with(
"INSERT OR IGNORE INTO user_audio_listens (file_name, user_id) VALUES (?, ?)",
("test_audio.ogg", 12345)
("test_audio.ogg", 12345),
)
@pytest.mark.asyncio
async def test_get_user_id_by_file_name(self, audio_repository):
"""Тест получения user_id по имени файла"""
audio_repository._execute_query_with_result.return_value = [(12345,)]
result = await audio_repository.get_user_id_by_file_name("test_audio.ogg")
assert result == 12345
audio_repository._execute_query_with_result.assert_called_once_with(
"SELECT author_id FROM audio_message_reference WHERE file_name = ?", ("test_audio.ogg",)
"SELECT author_id FROM audio_message_reference WHERE file_name = ?",
("test_audio.ogg",),
)
@pytest.mark.asyncio
async def test_get_user_id_by_file_name_not_found(self, audio_repository):
"""Тест получения user_id по имени файла когда файл не найден"""
audio_repository._execute_query_with_result.return_value = []
result = await audio_repository.get_user_id_by_file_name("nonexistent.ogg")
assert result is None
@pytest.mark.asyncio
async def test_get_date_by_file_name(self, audio_repository):
"""Тест получения даты по имени файла"""
timestamp = 1642404600 # 2022-01-17 10:30:00
audio_repository._execute_query_with_result.return_value = [(timestamp,)]
result = await audio_repository.get_date_by_file_name("test_audio.ogg")
# Должна вернуться читаемая дата
assert result == "17.01.2022 10:30"
audio_repository._execute_query_with_result.assert_called_once_with(
"SELECT date_added FROM audio_message_reference WHERE file_name = ?", ("test_audio.ogg",)
"SELECT date_added FROM audio_message_reference WHERE file_name = ?",
("test_audio.ogg",),
)
@pytest.mark.asyncio
async def test_get_date_by_file_name_not_found(self, audio_repository):
"""Тест получения даты по имени файла когда файл не найден"""
audio_repository._execute_query_with_result.return_value = []
result = await audio_repository.get_date_by_file_name("nonexistent.ogg")
assert result is None
@pytest.mark.asyncio
async def test_refresh_listen_audio(self, audio_repository):
"""Тест очистки записей прослушивания пользователя"""
await audio_repository.refresh_listen_audio(12345)
audio_repository._execute_query.assert_called_once_with(
"DELETE FROM user_audio_listens WHERE user_id = ?", (12345,)
)
@pytest.mark.asyncio
async def test_delete_listen_count_for_user(self, audio_repository):
"""Тест удаления данных о прослушанных аудио пользователя"""
await audio_repository.delete_listen_count_for_user(12345)
audio_repository._execute_query.assert_called_once_with(
"DELETE FROM user_audio_listens WHERE user_id = ?", (12345,)
)
@pytest.mark.asyncio
async def test_set_user_id_and_message_id_for_voice_bot_success(self, audio_repository):
async def test_set_user_id_and_message_id_for_voice_bot_success(
self, audio_repository
):
"""Тест успешной установки связи для voice bot"""
result = await audio_repository.set_user_id_and_message_id_for_voice_bot(123, 456)
result = await audio_repository.set_user_id_and_message_id_for_voice_bot(
123, 456
)
assert result is True
audio_repository._execute_query.assert_called_once_with(
"INSERT OR IGNORE INTO audio_moderate (user_id, message_id) VALUES (?, ?)",
(456, 123)
(456, 123),
)
@pytest.mark.asyncio
async def test_set_user_id_and_message_id_for_voice_bot_exception(self, audio_repository):
async def test_set_user_id_and_message_id_for_voice_bot_exception(
self, audio_repository
):
"""Тест установки связи для voice bot при ошибке"""
audio_repository._execute_query.side_effect = Exception("Database error")
result = await audio_repository.set_user_id_and_message_id_for_voice_bot(123, 456)
result = await audio_repository.set_user_id_and_message_id_for_voice_bot(
123, 456
)
assert result is False
@pytest.mark.asyncio
async def test_get_user_id_by_message_id_for_voice_bot(self, audio_repository):
"""Тест получения user_id по message_id для voice bot"""
audio_repository._execute_query_with_result.return_value = [(456,)]
result = await audio_repository.get_user_id_by_message_id_for_voice_bot(123)
assert result == 456
audio_repository._execute_query_with_result.assert_called_once_with(
"SELECT user_id FROM audio_moderate WHERE message_id = ?", (123,)
)
@pytest.mark.asyncio
async def test_get_user_id_by_message_id_for_voice_bot_not_found(self, audio_repository):
async def test_get_user_id_by_message_id_for_voice_bot_not_found(
self, audio_repository
):
"""Тест получения user_id по message_id когда связь не найдена"""
audio_repository._execute_query_with_result.return_value = []
result = await audio_repository.get_user_id_by_message_id_for_voice_bot(123)
assert result is None
@pytest.mark.asyncio
async def test_delete_audio_moderate_record(self, audio_repository):
"""Тест удаления записи из таблицы audio_moderate"""
message_id = 12345
await audio_repository.delete_audio_moderate_record(message_id)
audio_repository._execute_query.assert_called_once_with(
"DELETE FROM audio_moderate WHERE message_id = ?", (message_id,)
)
audio_repository.logger.info.assert_called_once_with(
f"Удалена запись из audio_moderate для message_id {message_id}"
)
@pytest.mark.asyncio
async def test_add_audio_record_logging(self, audio_repository, sample_audio_message):
async def test_add_audio_record_logging(
self, audio_repository, sample_audio_message
):
"""Тест логирования при добавлении аудио записи"""
await audio_repository.add_audio_record(sample_audio_message)
# Проверяем, что лог записан
audio_repository.logger.info.assert_called_once()
log_message = audio_repository.logger.info.call_args[0][0]
assert "Аудио добавлено" in log_message
assert "test_audio_123.ogg" in log_message
assert "12345" in log_message
@pytest.mark.asyncio
async def test_add_audio_record_simple_logging(self, audio_repository):
"""Тест логирования при упрощенном добавлении аудио записи"""
await audio_repository.add_audio_record_simple("test_audio.ogg", 12345, "2025-01-15 14:30:00")
await audio_repository.add_audio_record_simple(
"test_audio.ogg", 12345, "2025-01-15 14:30:00"
)
# Проверяем, что лог записан
audio_repository.logger.info.assert_called_once()
log_message = audio_repository.logger.info.call_args[0][0]
assert "Аудио добавлено" in log_message
assert "test_audio.ogg" in log_message
assert "12345" in log_message
@pytest.mark.asyncio
async def test_get_date_by_file_name_logging(self, audio_repository):
"""Тест логирования при получении даты по имени файла"""
timestamp = 1642404600 # 2022-01-17 10:30:00
audio_repository._execute_query_with_result.return_value = [(timestamp,)]
await audio_repository.get_date_by_file_name("test_audio.ogg")
# Проверяем, что лог записан
audio_repository.logger.info.assert_called_once()
log_message = audio_repository.logger.info.call_args[0][0]
@@ -387,20 +419,20 @@ class TestAudioRepository:
class TestAudioRepositoryIntegration:
"""Интеграционные тесты для AudioRepository"""
@pytest.fixture
def real_audio_repository(self):
"""Реальный экземпляр AudioRepository для интеграционных тестов"""
# Здесь можно создать реальное подключение к тестовой БД
# Но для простоты используем мок
return Mock()
@pytest.mark.asyncio
async def test_full_audio_workflow(self, real_audio_repository):
"""Тест полного рабочего процесса с аудио"""
# Этот тест можно расширить для реальной БД
assert True # Placeholder для будущих интеграционных тестов
@pytest.mark.asyncio
async def test_foreign_keys_enabled(self, real_audio_repository):
"""Тест что внешние ключи включены"""