Remove obsolete configuration management and test settings files
- Deleted the `config.py` file responsible for managing bot configuration via environment variables and `.env` files, as it is no longer needed. - Removed the `test_settings.ini` file used for testing, which contained mock configuration data. - Cleaned up the project structure by eliminating unused files to enhance maintainability.
This commit is contained in:
@@ -1,91 +0,0 @@
|
||||
"""
|
||||
Configuration management for the Telegram bot.
|
||||
Supports both environment variables and .env files.
|
||||
"""
|
||||
|
||||
import os
|
||||
from typing import Dict, Any, Optional
|
||||
from dotenv import load_dotenv
|
||||
|
||||
|
||||
class ConfigManager:
|
||||
"""Manages bot configuration with environment variable support."""
|
||||
|
||||
def __init__(self, env_file: str = ".env"):
|
||||
self.env_file = env_file
|
||||
self._load_env()
|
||||
|
||||
def _load_env(self):
|
||||
"""Load configuration from .env file if exists."""
|
||||
# Load from .env file if exists
|
||||
if os.path.exists(self.env_file):
|
||||
load_dotenv(self.env_file)
|
||||
|
||||
def get(self, section: str, key: str, default: Any = None) -> str:
|
||||
"""Get configuration value with environment variable override."""
|
||||
# Check environment variable first
|
||||
env_key = f"{section.upper()}_{key.upper()}"
|
||||
env_value = os.getenv(env_key)
|
||||
if env_value is not None:
|
||||
return env_value
|
||||
|
||||
# Fall back to direct environment variable
|
||||
direct_env_value = os.getenv(key.upper())
|
||||
if direct_env_value is not None:
|
||||
return direct_env_value
|
||||
|
||||
return default
|
||||
|
||||
def getboolean(self, section: str, key: str, default: bool = False) -> bool:
|
||||
"""Get boolean configuration value."""
|
||||
value = self.get(section, key, str(default))
|
||||
if isinstance(value, bool):
|
||||
return value
|
||||
return value.lower() in ('true', '1', 'yes', 'on')
|
||||
|
||||
def getint(self, section: str, key: str, default: int = 0) -> int:
|
||||
"""Get integer configuration value."""
|
||||
value = self.get(section, key, str(default))
|
||||
try:
|
||||
return int(value)
|
||||
except (ValueError, TypeError):
|
||||
return default
|
||||
|
||||
def get_all_settings(self) -> Dict[str, Dict[str, Any]]:
|
||||
"""Get all settings as dictionary."""
|
||||
settings = {}
|
||||
|
||||
# Telegram секция
|
||||
settings['Telegram'] = {
|
||||
'bot_token': self.get('Telegram', 'bot_token', ''),
|
||||
'listen_bot_token': self.get('Telegram', 'listen_bot_token', ''),
|
||||
'test_bot_token': self.get('Telegram', 'test_bot_token', ''),
|
||||
'preview_link': self.getboolean('Telegram', 'preview_link', False),
|
||||
'main_public': self.get('Telegram', 'main_public', ''),
|
||||
'group_for_posts': self.getint('Telegram', 'group_for_posts', 0),
|
||||
'group_for_message': self.getint('Telegram', 'group_for_message', 0),
|
||||
'group_for_logs': self.getint('Telegram', 'group_for_logs', 0),
|
||||
'important_logs': self.getint('Telegram', 'important_logs', 0),
|
||||
'archive': self.getint('Telegram', 'archive', 0),
|
||||
'test_group': self.getint('Telegram', 'test_group', 0)
|
||||
}
|
||||
|
||||
# Settings секция
|
||||
settings['Settings'] = {
|
||||
'logs': self.getboolean('Settings', 'logs', False),
|
||||
'test': self.getboolean('Settings', 'test', False)
|
||||
}
|
||||
|
||||
return settings
|
||||
|
||||
|
||||
# Global config instance
|
||||
_config_instance: Optional[ConfigManager] = None
|
||||
|
||||
|
||||
def get_config() -> ConfigManager:
|
||||
"""Get global configuration instance."""
|
||||
global _config_instance
|
||||
if _config_instance is None:
|
||||
_config_instance = ConfigManager()
|
||||
return _config_instance
|
||||
@@ -1,13 +0,0 @@
|
||||
[Telegram]
|
||||
bot_token = test_token_123
|
||||
preview_link = false
|
||||
main_public = @test
|
||||
group_for_posts = -1001234567890
|
||||
group_for_message = -1001234567891
|
||||
group_for_logs = -1001234567893
|
||||
important_logs = -1001234567894
|
||||
test_channel = -1001234567895
|
||||
|
||||
[Settings]
|
||||
logs = true
|
||||
test = false
|
||||
@@ -1,6 +1,7 @@
|
||||
import pytest
|
||||
from unittest.mock import Mock, AsyncMock, patch
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
from helper_bot.handlers.voice.services import VoiceBotService
|
||||
from helper_bot.handlers.voice.exceptions import VoiceMessageError, AudioProcessingError
|
||||
@@ -88,6 +89,57 @@ class TestVoiceBotService:
|
||||
voice_service.clear_user_listenings(123)
|
||||
|
||||
mock_bot_db.delete_listen_count_for_user.assert_called_once_with(123)
|
||||
|
||||
def test_get_remaining_audio_count_success(self, voice_service, mock_bot_db):
|
||||
"""Тест получения количества оставшихся аудио"""
|
||||
mock_bot_db.check_listen_audio.return_value = ['audio1', 'audio2', 'audio3']
|
||||
|
||||
result = voice_service.get_remaining_audio_count(123)
|
||||
|
||||
assert result == 3
|
||||
mock_bot_db.check_listen_audio.assert_called_once_with(user_id=123)
|
||||
|
||||
def test_get_remaining_audio_count_zero(self, voice_service, mock_bot_db):
|
||||
"""Тест получения количества оставшихся аудио когда их нет"""
|
||||
mock_bot_db.check_listen_audio.return_value = []
|
||||
|
||||
result = voice_service.get_remaining_audio_count(123)
|
||||
|
||||
assert result == 0
|
||||
mock_bot_db.check_listen_audio.assert_called_once_with(user_id=123)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_send_welcome_messages_success(self, voice_service, mock_bot_db, mock_settings):
|
||||
"""Тест успешной отправки приветственных сообщений"""
|
||||
mock_message = Mock()
|
||||
mock_message.from_user.id = 123
|
||||
mock_message.answer = AsyncMock()
|
||||
mock_message.answer.return_value = Mock()
|
||||
mock_message.answer_sticker = AsyncMock()
|
||||
|
||||
with patch.object(voice_service, 'get_welcome_sticker') as mock_sticker:
|
||||
mock_sticker.return_value = 'test_sticker.tgs'
|
||||
|
||||
await voice_service.send_welcome_messages(mock_message, '😊')
|
||||
|
||||
# Проверяем, что сообщения отправлены
|
||||
assert mock_message.answer.call_count >= 1
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_send_welcome_messages_no_sticker(self, voice_service, mock_bot_db, mock_settings):
|
||||
"""Тест отправки приветственных сообщений без стикера"""
|
||||
mock_message = Mock()
|
||||
mock_message.from_user.id = 123
|
||||
mock_message.answer = AsyncMock()
|
||||
mock_message.answer.return_value = Mock()
|
||||
|
||||
with patch.object(voice_service, 'get_welcome_sticker') as mock_sticker:
|
||||
mock_sticker.return_value = None
|
||||
|
||||
await voice_service.send_welcome_messages(mock_message, '😊')
|
||||
|
||||
# Проверяем, что сообщения отправлены
|
||||
assert mock_message.answer.call_count >= 1
|
||||
|
||||
|
||||
class TestVoiceHandlers:
|
||||
@@ -149,6 +201,7 @@ class TestUtils:
|
||||
"""Тест валидации голосового сообщения"""
|
||||
mock_message = Mock()
|
||||
mock_message.content_type = 'voice'
|
||||
mock_message.voice = Mock()
|
||||
|
||||
result = validate_voice_message(mock_message)
|
||||
|
||||
@@ -171,6 +224,22 @@ class TestUtils:
|
||||
|
||||
assert result == "😊"
|
||||
mock_bot_db.check_emoji_for_user.assert_called_once_with(123)
|
||||
|
||||
def test_get_user_emoji_safe_none(self, mock_bot_db):
|
||||
"""Тест безопасного получения эмодзи когда его нет"""
|
||||
mock_bot_db.check_emoji_for_user.return_value = None
|
||||
|
||||
result = get_user_emoji_safe(mock_bot_db, 123)
|
||||
|
||||
assert result == "😊"
|
||||
|
||||
def test_get_user_emoji_safe_error(self, mock_bot_db):
|
||||
"""Тест безопасного получения эмодзи при ошибке"""
|
||||
mock_bot_db.check_emoji_for_user.return_value = "Ошибка"
|
||||
|
||||
result = get_user_emoji_safe(mock_bot_db, 123)
|
||||
|
||||
assert result == "Ошибка"
|
||||
|
||||
|
||||
class TestExceptions:
|
||||
|
||||
170
tests/test_voice_constants.py
Normal file
170
tests/test_voice_constants.py
Normal file
@@ -0,0 +1,170 @@
|
||||
import pytest
|
||||
from helper_bot.handlers.voice.constants import (
|
||||
BUTTON_COMMAND_MAPPING,
|
||||
COMMAND_MAPPING,
|
||||
CALLBACK_COMMAND_MAPPING,
|
||||
VOICE_BOT_NAME,
|
||||
STATE_START,
|
||||
STATE_STANDUP_WRITE,
|
||||
BTN_SPEAK,
|
||||
BTN_LISTEN,
|
||||
CMD_START,
|
||||
CMD_HELP,
|
||||
CMD_RESTART,
|
||||
CMD_EMOJI,
|
||||
CMD_REFRESH,
|
||||
CALLBACK_SAVE,
|
||||
CALLBACK_DELETE
|
||||
)
|
||||
|
||||
|
||||
class TestVoiceConstants:
|
||||
"""Тесты для констант voice модуля"""
|
||||
|
||||
def test_button_command_mapping_structure(self):
|
||||
"""Тест структуры BUTTON_COMMAND_MAPPING"""
|
||||
assert isinstance(BUTTON_COMMAND_MAPPING, dict)
|
||||
assert len(BUTTON_COMMAND_MAPPING) > 0
|
||||
|
||||
# Проверяем, что все значения являются строками
|
||||
for key, value in BUTTON_COMMAND_MAPPING.items():
|
||||
assert isinstance(key, str)
|
||||
assert isinstance(value, str)
|
||||
|
||||
def test_button_command_mapping_specific_values(self):
|
||||
"""Тест конкретных значений в BUTTON_COMMAND_MAPPING"""
|
||||
assert "🎤Высказаться" in BUTTON_COMMAND_MAPPING
|
||||
assert "🎧Послушать" in BUTTON_COMMAND_MAPPING
|
||||
|
||||
assert BUTTON_COMMAND_MAPPING["🎤Высказаться"] == "voice_speak"
|
||||
assert BUTTON_COMMAND_MAPPING["🎧Послушать"] == "voice_listen"
|
||||
|
||||
def test_command_mapping_structure(self):
|
||||
"""Тест структуры COMMAND_MAPPING"""
|
||||
assert isinstance(COMMAND_MAPPING, dict)
|
||||
assert len(COMMAND_MAPPING) > 0
|
||||
|
||||
# Проверяем, что все значения являются строками
|
||||
for key, value in COMMAND_MAPPING.items():
|
||||
assert isinstance(key, str)
|
||||
assert isinstance(value, str)
|
||||
|
||||
def test_command_mapping_specific_values(self):
|
||||
"""Тест конкретных значений в COMMAND_MAPPING"""
|
||||
assert "start" in COMMAND_MAPPING
|
||||
assert "help" in COMMAND_MAPPING
|
||||
assert "restart" in COMMAND_MAPPING
|
||||
assert "emoji" in COMMAND_MAPPING
|
||||
assert "refresh" in COMMAND_MAPPING
|
||||
|
||||
assert COMMAND_MAPPING["start"] == "voice_start"
|
||||
assert COMMAND_MAPPING["help"] == "voice_help"
|
||||
assert COMMAND_MAPPING["restart"] == "voice_restart"
|
||||
assert COMMAND_MAPPING["emoji"] == "voice_emoji"
|
||||
assert COMMAND_MAPPING["refresh"] == "voice_refresh"
|
||||
|
||||
def test_callback_command_mapping_structure(self):
|
||||
"""Тест структуры CALLBACK_COMMAND_MAPPING"""
|
||||
assert isinstance(CALLBACK_COMMAND_MAPPING, dict)
|
||||
assert len(CALLBACK_COMMAND_MAPPING) > 0
|
||||
|
||||
# Проверяем, что все значения являются строками
|
||||
for key, value in CALLBACK_COMMAND_MAPPING.items():
|
||||
assert isinstance(key, str)
|
||||
assert isinstance(value, str)
|
||||
|
||||
def test_callback_command_mapping_specific_values(self):
|
||||
"""Тест конкретных значений в CALLBACK_COMMAND_MAPPING"""
|
||||
assert "save" in CALLBACK_COMMAND_MAPPING
|
||||
assert "delete" in CALLBACK_COMMAND_MAPPING
|
||||
|
||||
assert CALLBACK_COMMAND_MAPPING["save"] == "voice_save"
|
||||
assert CALLBACK_COMMAND_MAPPING["delete"] == "voice_delete"
|
||||
|
||||
def test_voice_bot_name(self):
|
||||
"""Тест VOICE_BOT_NAME"""
|
||||
assert isinstance(VOICE_BOT_NAME, str)
|
||||
assert len(VOICE_BOT_NAME) > 0
|
||||
assert "voice" in VOICE_BOT_NAME.lower()
|
||||
|
||||
def test_state_constants(self):
|
||||
"""Тест констант состояний"""
|
||||
assert isinstance(STATE_START, str)
|
||||
assert isinstance(STATE_STANDUP_WRITE, str)
|
||||
assert len(STATE_START) > 0
|
||||
assert len(STATE_STANDUP_WRITE) > 0
|
||||
|
||||
def test_button_constants(self):
|
||||
"""Тест констант кнопок"""
|
||||
assert isinstance(BTN_SPEAK, str)
|
||||
assert isinstance(BTN_LISTEN, str)
|
||||
assert len(BTN_SPEAK) > 0
|
||||
assert len(BTN_LISTEN) > 0
|
||||
|
||||
def test_command_constants(self):
|
||||
"""Тест констант команд"""
|
||||
assert isinstance(CMD_START, str)
|
||||
assert isinstance(CMD_HELP, str)
|
||||
assert isinstance(CMD_RESTART, str)
|
||||
assert isinstance(CMD_EMOJI, str)
|
||||
assert isinstance(CMD_REFRESH, str)
|
||||
|
||||
assert CMD_START == "start"
|
||||
assert CMD_HELP == "help"
|
||||
assert CMD_RESTART == "restart"
|
||||
assert CMD_EMOJI == "emoji"
|
||||
assert CMD_REFRESH == "refresh"
|
||||
|
||||
def test_callback_constants(self):
|
||||
"""Тест констант callback"""
|
||||
assert isinstance(CALLBACK_SAVE, str)
|
||||
assert isinstance(CALLBACK_DELETE, str)
|
||||
|
||||
assert CALLBACK_SAVE == "save"
|
||||
assert CALLBACK_DELETE == "delete"
|
||||
|
||||
def test_mapping_consistency(self):
|
||||
"""Тест согласованности маппингов"""
|
||||
# Проверяем, что все ключи в маппингах соответствуют константам
|
||||
assert "🎤Высказаться" in BUTTON_COMMAND_MAPPING
|
||||
assert "🎧Послушать" in BUTTON_COMMAND_MAPPING
|
||||
|
||||
assert "start" in COMMAND_MAPPING
|
||||
assert "help" in COMMAND_MAPPING
|
||||
assert "restart" in COMMAND_MAPPING
|
||||
assert "emoji" in COMMAND_MAPPING
|
||||
assert "refresh" in COMMAND_MAPPING
|
||||
|
||||
assert "save" in CALLBACK_COMMAND_MAPPING
|
||||
assert "delete" in CALLBACK_COMMAND_MAPPING
|
||||
|
||||
def test_mapping_values_format(self):
|
||||
"""Тест формата значений в маппингах"""
|
||||
# Проверяем, что все значения начинаются с 'voice_'
|
||||
for value in BUTTON_COMMAND_MAPPING.values():
|
||||
assert value.startswith("voice_")
|
||||
|
||||
for value in COMMAND_MAPPING.values():
|
||||
assert value.startswith("voice_")
|
||||
|
||||
for value in CALLBACK_COMMAND_MAPPING.values():
|
||||
assert value.startswith("voice_")
|
||||
|
||||
def test_no_duplicate_values(self):
|
||||
"""Тест отсутствия дублирующихся значений"""
|
||||
button_values = list(BUTTON_COMMAND_MAPPING.values())
|
||||
command_values = list(COMMAND_MAPPING.values())
|
||||
callback_values = list(CALLBACK_COMMAND_MAPPING.values())
|
||||
|
||||
# Проверяем, что нет дублирующихся значений в каждом маппинге
|
||||
assert len(button_values) == len(set(button_values))
|
||||
assert len(command_values) == len(set(command_values))
|
||||
assert len(callback_values) == len(set(callback_values))
|
||||
|
||||
# Проверяем, что нет дублирующихся значений между маппингами
|
||||
all_values = button_values + command_values + callback_values
|
||||
assert len(all_values) == len(set(all_values))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main([__file__])
|
||||
211
tests/test_voice_exceptions.py
Normal file
211
tests/test_voice_exceptions.py
Normal file
@@ -0,0 +1,211 @@
|
||||
import pytest
|
||||
from helper_bot.handlers.voice.exceptions import (
|
||||
VoiceMessageError,
|
||||
AudioProcessingError,
|
||||
VoiceBotError
|
||||
)
|
||||
|
||||
|
||||
class TestVoiceExceptions:
|
||||
"""Тесты для исключений voice модуля"""
|
||||
|
||||
def test_voice_message_error_inheritance(self):
|
||||
"""Тест наследования VoiceMessageError"""
|
||||
assert issubclass(VoiceMessageError, Exception)
|
||||
|
||||
def test_voice_message_error_message(self):
|
||||
"""Тест сообщения VoiceMessageError"""
|
||||
error_message = "Тестовая ошибка голосового сообщения"
|
||||
error = VoiceMessageError(error_message)
|
||||
|
||||
assert str(error) == error_message
|
||||
assert error.args == (error_message,)
|
||||
|
||||
def test_voice_message_error_empty_message(self):
|
||||
"""Тест VoiceMessageError с пустым сообщением"""
|
||||
error = VoiceMessageError("")
|
||||
|
||||
assert str(error) == ""
|
||||
assert error.args == ("",)
|
||||
|
||||
def test_voice_message_error_none_message(self):
|
||||
"""Тест VoiceMessageError с None сообщением"""
|
||||
error = VoiceMessageError(None)
|
||||
|
||||
assert str(error) == "None"
|
||||
assert error.args == (None,)
|
||||
|
||||
def test_voice_message_error_multiple_args(self):
|
||||
"""Тест VoiceMessageError с несколькими аргументами"""
|
||||
error = VoiceMessageError("Ошибка", "Дополнительная информация")
|
||||
|
||||
assert str(error) == "('Ошибка', 'Дополнительная информация')"
|
||||
assert error.args == ("Ошибка", "Дополнительная информация")
|
||||
|
||||
def test_audio_processing_error_inheritance(self):
|
||||
"""Тест наследования AudioProcessingError"""
|
||||
assert issubclass(AudioProcessingError, Exception)
|
||||
|
||||
def test_audio_processing_error_message(self):
|
||||
"""Тест сообщения AudioProcessingError"""
|
||||
error_message = "Ошибка обработки аудио файла"
|
||||
error = AudioProcessingError(error_message)
|
||||
|
||||
assert str(error) == error_message
|
||||
assert error.args == (error_message,)
|
||||
|
||||
def test_audio_processing_error_empty_message(self):
|
||||
"""Тест AudioProcessingError с пустым сообщением"""
|
||||
error = AudioProcessingError("")
|
||||
|
||||
assert str(error) == ""
|
||||
assert error.args == ("",)
|
||||
|
||||
def test_audio_processing_error_none_message(self):
|
||||
"""Тест AudioProcessingError с None сообщением"""
|
||||
error = AudioProcessingError(None)
|
||||
|
||||
assert str(error) == "None"
|
||||
assert error.args == (None,)
|
||||
|
||||
def test_voice_bot_error_inheritance(self):
|
||||
"""Тест наследования VoiceBotError"""
|
||||
assert issubclass(VoiceBotError, Exception)
|
||||
|
||||
def test_voice_bot_error_message(self):
|
||||
"""Тест сообщения VoiceBotError"""
|
||||
error_message = "Общая ошибка voice бота"
|
||||
error = VoiceBotError(error_message)
|
||||
|
||||
assert str(error) == error_message
|
||||
assert error.args == (error_message,)
|
||||
|
||||
def test_exception_hierarchy(self):
|
||||
"""Тест иерархии исключений"""
|
||||
# Проверяем, что все исключения наследуются от Exception
|
||||
assert issubclass(VoiceMessageError, Exception)
|
||||
assert issubclass(AudioProcessingError, Exception)
|
||||
assert issubclass(VoiceBotError, Exception)
|
||||
|
||||
# Проверяем, что VoiceMessageError и AudioProcessingError наследуются от VoiceBotError
|
||||
assert issubclass(VoiceMessageError, VoiceBotError)
|
||||
assert issubclass(AudioProcessingError, VoiceBotError)
|
||||
|
||||
# Проверяем, что VoiceBotError не наследуется от других исключений
|
||||
assert not issubclass(VoiceBotError, VoiceMessageError)
|
||||
assert not issubclass(VoiceBotError, AudioProcessingError)
|
||||
|
||||
# Проверяем, что VoiceMessageError и AudioProcessingError не наследуются друг от друга
|
||||
assert not issubclass(VoiceMessageError, AudioProcessingError)
|
||||
assert not issubclass(AudioProcessingError, VoiceMessageError)
|
||||
|
||||
def test_exception_creation_without_args(self):
|
||||
"""Тест создания исключений без аргументов"""
|
||||
# Должно работать без аргументов
|
||||
voice_error = VoiceMessageError()
|
||||
audio_error = AudioProcessingError()
|
||||
bot_error = VoiceBotError()
|
||||
|
||||
assert str(voice_error) == ""
|
||||
assert str(audio_error) == ""
|
||||
assert str(bot_error) == ""
|
||||
|
||||
def test_exception_creation_with_int(self):
|
||||
"""Тест создания исключений с числовыми аргументами"""
|
||||
voice_error = VoiceMessageError(123)
|
||||
audio_error = AudioProcessingError(456)
|
||||
bot_error = VoiceBotError(789)
|
||||
|
||||
assert str(voice_error) == "123"
|
||||
assert str(audio_error) == "456"
|
||||
assert str(bot_error) == "789"
|
||||
|
||||
def test_exception_creation_with_list(self):
|
||||
"""Тест создания исключений со списками"""
|
||||
error_list = ["Ошибка 1", "Ошибка 2"]
|
||||
voice_error = VoiceMessageError(error_list)
|
||||
audio_error = AudioProcessingError(error_list)
|
||||
bot_error = VoiceBotError(error_list)
|
||||
|
||||
assert str(voice_error) == str(error_list)
|
||||
assert str(audio_error) == str(error_list)
|
||||
assert str(bot_error) == str(error_list)
|
||||
|
||||
def test_exception_creation_with_dict(self):
|
||||
"""Тест создания исключений со словарями"""
|
||||
error_dict = {"code": 500, "message": "Internal error"}
|
||||
voice_error = VoiceMessageError(error_dict)
|
||||
audio_error = AudioProcessingError(error_dict)
|
||||
bot_error = VoiceBotError(error_dict)
|
||||
|
||||
assert str(voice_error) == str(error_dict)
|
||||
assert str(audio_error) == str(error_dict)
|
||||
assert str(bot_error) == str(error_dict)
|
||||
|
||||
def test_exception_attributes(self):
|
||||
"""Тест атрибутов исключений"""
|
||||
error_message = "Тестовая ошибка"
|
||||
voice_error = VoiceMessageError(error_message)
|
||||
audio_error = AudioProcessingError(error_message)
|
||||
bot_error = VoiceBotError(error_message)
|
||||
|
||||
# Проверяем, что исключения имеют атрибут args
|
||||
assert hasattr(voice_error, 'args')
|
||||
assert hasattr(audio_error, 'args')
|
||||
assert hasattr(bot_error, 'args')
|
||||
|
||||
# Проверяем, что args содержит переданное сообщение
|
||||
assert voice_error.args == (error_message,)
|
||||
assert audio_error.args == (error_message,)
|
||||
assert bot_error.args == (error_message,)
|
||||
|
||||
def test_exception_string_representation(self):
|
||||
"""Тест строкового представления исключений"""
|
||||
error_message = "Тестовая ошибка"
|
||||
voice_error = VoiceMessageError(error_message)
|
||||
audio_error = AudioProcessingError(error_message)
|
||||
bot_error = VoiceBotError(error_message)
|
||||
|
||||
# Проверяем, что str() возвращает сообщение
|
||||
assert str(voice_error) == error_message
|
||||
assert str(audio_error) == error_message
|
||||
assert str(bot_error) == error_message
|
||||
|
||||
# Проверяем, что repr() содержит имя класса
|
||||
assert "VoiceMessageError" in repr(voice_error)
|
||||
assert "AudioProcessingError" in repr(audio_error)
|
||||
assert "VoiceBotError" in repr(bot_error)
|
||||
|
||||
def test_exception_equality(self):
|
||||
"""Тест равенства исключений"""
|
||||
error1 = VoiceMessageError("Ошибка")
|
||||
error2 = VoiceMessageError("Ошибка")
|
||||
error3 = VoiceMessageError("Другая ошибка")
|
||||
|
||||
# Исключения с одинаковыми сообщениями не равны (разные объекты)
|
||||
assert error1 != error2
|
||||
assert error1 != error3
|
||||
|
||||
# Но их строковые представления равны
|
||||
assert str(error1) == str(error2)
|
||||
assert str(error1) != str(error3)
|
||||
|
||||
def test_exception_inheritance_chain(self):
|
||||
"""Тест цепочки наследования исключений"""
|
||||
# Проверяем, что все исключения являются экземплярами Exception
|
||||
voice_error = VoiceMessageError("Ошибка")
|
||||
audio_error = AudioProcessingError("Ошибка")
|
||||
bot_error = VoiceBotError("Ошибка")
|
||||
|
||||
assert isinstance(voice_error, Exception)
|
||||
assert isinstance(audio_error, Exception)
|
||||
assert isinstance(bot_error, Exception)
|
||||
|
||||
# Проверяем, что исключения являются экземплярами своих классов
|
||||
assert isinstance(voice_error, VoiceMessageError)
|
||||
assert isinstance(audio_error, AudioProcessingError)
|
||||
assert isinstance(bot_error, VoiceBotError)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main([__file__])
|
||||
117
tests/test_voice_handler.py
Normal file
117
tests/test_voice_handler.py
Normal file
@@ -0,0 +1,117 @@
|
||||
import pytest
|
||||
from unittest.mock import Mock, AsyncMock, patch, MagicMock
|
||||
from aiogram import types
|
||||
from aiogram.fsm.context import FSMContext
|
||||
|
||||
from helper_bot.handlers.voice.voice_handler import VoiceHandlers
|
||||
from helper_bot.handlers.voice.constants import STATE_START, STATE_STANDUP_WRITE
|
||||
|
||||
|
||||
class TestVoiceHandler:
|
||||
"""Тесты для VoiceHandler"""
|
||||
|
||||
@pytest.fixture
|
||||
def mock_db(self):
|
||||
"""Мок для базы данных"""
|
||||
mock_db = Mock()
|
||||
mock_db.settings = {
|
||||
'Telegram': {
|
||||
'group_for_logs': 'test_logs_chat',
|
||||
'group_for_posts': 'test_posts_chat',
|
||||
'preview_link': True
|
||||
}
|
||||
}
|
||||
return mock_db
|
||||
|
||||
@pytest.fixture
|
||||
def mock_settings(self):
|
||||
"""Мок для настроек"""
|
||||
return {
|
||||
'Telegram': {
|
||||
'group_for_logs': 'test_logs_chat',
|
||||
'group_for_posts': 'test_posts_chat',
|
||||
'preview_link': True
|
||||
}
|
||||
}
|
||||
|
||||
@pytest.fixture
|
||||
def mock_message(self):
|
||||
"""Мок для сообщения"""
|
||||
message = Mock(spec=types.Message)
|
||||
message.from_user = Mock()
|
||||
message.from_user.id = 123
|
||||
message.from_user.first_name = "Test"
|
||||
message.from_user.full_name = "Test User"
|
||||
message.chat = Mock()
|
||||
message.chat.id = 456
|
||||
message.answer = AsyncMock()
|
||||
message.forward = AsyncMock()
|
||||
return message
|
||||
|
||||
@pytest.fixture
|
||||
def mock_state(self):
|
||||
"""Мок для состояния FSM"""
|
||||
state = Mock(spec=FSMContext)
|
||||
state.set_state = AsyncMock()
|
||||
return state
|
||||
|
||||
@pytest.fixture
|
||||
def voice_handler(self, mock_db, mock_settings):
|
||||
"""Экземпляр VoiceHandler для тестов"""
|
||||
return VoiceHandlers(mock_db, mock_settings)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_voice_bot_button_handler_welcome_received(self, voice_handler, mock_message, mock_state, mock_db, mock_settings):
|
||||
"""Тест обработчика кнопки когда приветствие уже получено"""
|
||||
mock_db.check_voice_bot_welcome_received.return_value = True
|
||||
|
||||
with patch.object(voice_handler, 'restart_function') as mock_restart:
|
||||
await voice_handler.voice_bot_button_handler(mock_message, mock_state, mock_db, mock_settings)
|
||||
|
||||
mock_db.check_voice_bot_welcome_received.assert_called_once_with(123)
|
||||
mock_restart.assert_called_once_with(mock_message, mock_state, mock_db, mock_settings)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_voice_bot_button_handler_welcome_not_received(self, voice_handler, mock_message, mock_state, mock_db, mock_settings):
|
||||
"""Тест обработчика кнопки когда приветствие не получено"""
|
||||
mock_db.check_voice_bot_welcome_received.return_value = False
|
||||
|
||||
with patch.object(voice_handler, 'start') as mock_start:
|
||||
await voice_handler.voice_bot_button_handler(mock_message, mock_state, mock_db, mock_settings)
|
||||
|
||||
mock_db.check_voice_bot_welcome_received.assert_called_once_with(123)
|
||||
mock_start.assert_called_once_with(mock_message, mock_state, mock_db, mock_settings)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_voice_bot_button_handler_exception(self, voice_handler, mock_message, mock_state, mock_db, mock_settings):
|
||||
"""Тест обработчика кнопки при исключении"""
|
||||
mock_db.check_voice_bot_welcome_received.side_effect = Exception("Test error")
|
||||
|
||||
with patch.object(voice_handler, 'start') as mock_start:
|
||||
await voice_handler.voice_bot_button_handler(mock_message, mock_state, mock_db, mock_settings)
|
||||
|
||||
mock_start.assert_called_once_with(mock_message, mock_state, mock_db, mock_settings)
|
||||
|
||||
# Упрощенные тесты для основных функций
|
||||
@pytest.mark.asyncio
|
||||
async def test_standup_write(self, voice_handler, mock_message, mock_state, mock_db, mock_settings):
|
||||
"""Тест функции standup_write"""
|
||||
with patch('helper_bot.handlers.voice.voice_handler.messages.get_message') as mock_get_message:
|
||||
mock_get_message.return_value = "Record voice message"
|
||||
|
||||
await voice_handler.standup_write(mock_message, mock_state, mock_db, mock_settings)
|
||||
|
||||
mock_state.set_state.assert_called_once_with(STATE_STANDUP_WRITE)
|
||||
mock_message.answer.assert_called_once_with(
|
||||
text="Record voice message",
|
||||
reply_markup=types.ReplyKeyboardRemove()
|
||||
)
|
||||
|
||||
def test_setup_handlers(self, voice_handler):
|
||||
"""Тест настройки обработчиков"""
|
||||
# Проверяем, что роутер содержит обработчики
|
||||
assert len(voice_handler.router.message.handlers) > 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main([__file__])
|
||||
231
tests/test_voice_services.py
Normal file
231
tests/test_voice_services.py
Normal file
@@ -0,0 +1,231 @@
|
||||
import pytest
|
||||
from unittest.mock import Mock, AsyncMock, patch, MagicMock
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
from helper_bot.handlers.voice.services import VoiceBotService
|
||||
from helper_bot.handlers.voice.exceptions import VoiceMessageError, AudioProcessingError
|
||||
|
||||
|
||||
class TestVoiceBotService:
|
||||
"""Тесты для VoiceBotService"""
|
||||
|
||||
@pytest.fixture
|
||||
def mock_bot_db(self):
|
||||
"""Мок для базы данных"""
|
||||
mock_db = Mock()
|
||||
mock_db.settings = {
|
||||
'Settings': {'logs': True},
|
||||
'Telegram': {'important_logs': 'test_chat_id'}
|
||||
}
|
||||
return mock_db
|
||||
|
||||
@pytest.fixture
|
||||
def mock_settings(self):
|
||||
"""Мок для настроек"""
|
||||
return {
|
||||
'Settings': {'logs': True},
|
||||
'Telegram': {'preview_link': True}
|
||||
}
|
||||
|
||||
@pytest.fixture
|
||||
def voice_service(self, mock_bot_db, mock_settings):
|
||||
"""Экземпляр VoiceBotService для тестов"""
|
||||
return VoiceBotService(mock_bot_db, mock_settings)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_welcome_sticker_success(self, voice_service, mock_settings):
|
||||
"""Тест успешного получения стикера"""
|
||||
with patch('pathlib.Path.rglob') as mock_rglob:
|
||||
mock_rglob.return_value = ['/path/to/sticker1.tgs', '/path/to/sticker2.tgs']
|
||||
|
||||
sticker = await voice_service.get_welcome_sticker()
|
||||
|
||||
assert sticker is not None
|
||||
mock_rglob.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_welcome_sticker_no_stickers(self, voice_service, mock_settings):
|
||||
"""Тест получения стикера когда их нет"""
|
||||
with patch('pathlib.Path.rglob') as mock_rglob:
|
||||
mock_rglob.return_value = []
|
||||
|
||||
sticker = await voice_service.get_welcome_sticker()
|
||||
|
||||
assert sticker is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_welcome_sticker_only_webp_files(self, voice_service, mock_settings):
|
||||
"""Тест получения стикера когда есть только webp файлы"""
|
||||
with patch('pathlib.Path.rglob') as mock_rglob:
|
||||
mock_rglob.return_value = ['/path/to/sticker1.webp', '/path/to/sticker2.webp']
|
||||
|
||||
sticker = await voice_service.get_welcome_sticker()
|
||||
|
||||
# Проверяем, что стикер не None (метод ищет файлы по паттерну Hello_*)
|
||||
assert sticker is not None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_welcome_sticker_mixed_files(self, voice_service, mock_settings):
|
||||
"""Тест получения стикера когда есть смешанные файлы"""
|
||||
with patch('pathlib.Path.rglob') as mock_rglob:
|
||||
mock_rglob.return_value = [
|
||||
'/path/to/sticker1.webp',
|
||||
'/path/to/sticker2.tgs',
|
||||
'/path/to/sticker3.webp'
|
||||
]
|
||||
|
||||
sticker = await voice_service.get_welcome_sticker()
|
||||
|
||||
assert sticker is not None
|
||||
# Проверяем, что стикер не None (метод возвращает FSInputFile объект)
|
||||
|
||||
def test_get_random_audio_success(self, voice_service, mock_bot_db):
|
||||
"""Тест успешного получения случайного аудио"""
|
||||
mock_bot_db.check_listen_audio.return_value = ['audio1', 'audio2']
|
||||
mock_bot_db.get_user_id_by_file_name.return_value = 123
|
||||
mock_bot_db.get_date_by_file_name.return_value = '2025-01-01 12:00:00'
|
||||
mock_bot_db.check_emoji_for_user.return_value = '😊'
|
||||
|
||||
result = voice_service.get_random_audio(456)
|
||||
|
||||
assert result is not None
|
||||
assert len(result) == 3
|
||||
# Проверяем, что результат содержит ожидаемые данные, но не проверяем точное значение audio
|
||||
assert result[0] in ['audio1', 'audio2']
|
||||
assert result[1] == '2025-01-01 12:00:00'
|
||||
assert result[2] == '😊'
|
||||
|
||||
def test_get_random_audio_no_audio(self, voice_service, mock_bot_db):
|
||||
"""Тест получения аудио когда их нет"""
|
||||
mock_bot_db.check_listen_audio.return_value = []
|
||||
|
||||
result = voice_service.get_random_audio(456)
|
||||
|
||||
assert result is None
|
||||
|
||||
def test_get_random_audio_single_audio(self, voice_service, mock_bot_db):
|
||||
"""Тест получения аудио когда есть только одно"""
|
||||
mock_bot_db.check_listen_audio.return_value = ['audio1']
|
||||
mock_bot_db.get_user_id_by_file_name.return_value = 123
|
||||
mock_bot_db.get_date_by_file_name.return_value = '2025-01-01 12:00:00'
|
||||
mock_bot_db.check_emoji_for_user.return_value = '😊'
|
||||
|
||||
result = voice_service.get_random_audio(456)
|
||||
|
||||
assert result is not None
|
||||
assert len(result) == 3
|
||||
assert result[0] == 'audio1'
|
||||
|
||||
def test_mark_audio_as_listened_success(self, voice_service, mock_bot_db):
|
||||
"""Тест успешной пометки аудио как прослушанного"""
|
||||
voice_service.mark_audio_as_listened('test_audio', 123)
|
||||
|
||||
mock_bot_db.mark_listened_audio.assert_called_once_with('test_audio', user_id=123)
|
||||
|
||||
def test_clear_user_listenings_success(self, voice_service, mock_bot_db):
|
||||
"""Тест успешной очистки прослушиваний"""
|
||||
voice_service.clear_user_listenings(123)
|
||||
|
||||
mock_bot_db.delete_listen_count_for_user.assert_called_once_with(123)
|
||||
|
||||
def test_get_remaining_audio_count_success(self, voice_service, mock_bot_db):
|
||||
"""Тест получения количества оставшихся аудио"""
|
||||
mock_bot_db.check_listen_audio.return_value = ['audio1', 'audio2', 'audio3']
|
||||
|
||||
result = voice_service.get_remaining_audio_count(123)
|
||||
|
||||
assert result == 3
|
||||
mock_bot_db.check_listen_audio.assert_called_once_with(user_id=123)
|
||||
|
||||
def test_get_remaining_audio_count_zero(self, voice_service, mock_bot_db):
|
||||
"""Тест получения количества оставшихся аудио когда их нет"""
|
||||
mock_bot_db.check_listen_audio.return_value = []
|
||||
|
||||
result = voice_service.get_remaining_audio_count(123)
|
||||
|
||||
assert result == 0
|
||||
mock_bot_db.check_listen_audio.assert_called_once_with(user_id=123)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_send_welcome_messages_success(self, voice_service, mock_bot_db, mock_settings):
|
||||
"""Тест успешной отправки приветственных сообщений"""
|
||||
mock_message = Mock()
|
||||
mock_message.from_user.id = 123
|
||||
mock_message.answer = AsyncMock()
|
||||
mock_message.answer.return_value = Mock()
|
||||
mock_message.answer_sticker = AsyncMock()
|
||||
|
||||
with patch.object(voice_service, 'get_welcome_sticker') as mock_sticker:
|
||||
mock_sticker.return_value = 'test_sticker.tgs'
|
||||
|
||||
await voice_service.send_welcome_messages(mock_message, '😊')
|
||||
|
||||
# Проверяем, что сообщения отправлены
|
||||
assert mock_message.answer.call_count >= 1
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_send_welcome_messages_no_sticker(self, voice_service, mock_bot_db, mock_settings):
|
||||
"""Тест отправки приветственных сообщений без стикера"""
|
||||
mock_message = Mock()
|
||||
mock_message.from_user.id = 123
|
||||
mock_message.answer = AsyncMock()
|
||||
mock_message.answer.return_value = Mock()
|
||||
|
||||
with patch.object(voice_service, 'get_welcome_sticker') as mock_sticker:
|
||||
mock_sticker.return_value = None
|
||||
|
||||
await voice_service.send_welcome_messages(mock_message, '😊')
|
||||
|
||||
# Проверяем, что сообщения отправлены
|
||||
assert mock_message.answer.call_count >= 1
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_send_welcome_messages_with_sticker(self, voice_service, mock_bot_db, mock_settings):
|
||||
"""Тест отправки приветственных сообщений со стикером"""
|
||||
mock_message = Mock()
|
||||
mock_message.from_user.id = 123
|
||||
mock_message.answer = AsyncMock()
|
||||
mock_message.answer.return_value = Mock()
|
||||
mock_message.answer_sticker = AsyncMock()
|
||||
|
||||
with patch.object(voice_service, 'get_welcome_sticker') as mock_sticker:
|
||||
mock_sticker.return_value = 'test_sticker.tgs'
|
||||
|
||||
await voice_service.send_welcome_messages(mock_message, '😊')
|
||||
|
||||
# Проверяем, что сообщения отправлены
|
||||
assert mock_message.answer.call_count >= 1
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_welcome_sticker_with_tgs_files(self, voice_service, mock_settings):
|
||||
"""Тест получения стикера когда есть .tgs файлы"""
|
||||
with patch('pathlib.Path.rglob') as mock_rglob:
|
||||
mock_rglob.return_value = ['/path/to/sticker1.tgs', '/path/to/sticker2.tgs']
|
||||
|
||||
sticker = await voice_service.get_welcome_sticker()
|
||||
|
||||
assert sticker is not None
|
||||
# Проверяем, что стикер не None (метод возвращает FSInputFile объект)
|
||||
|
||||
def test_service_initialization(self, mock_bot_db, mock_settings):
|
||||
"""Тест инициализации сервиса"""
|
||||
service = VoiceBotService(mock_bot_db, mock_settings)
|
||||
|
||||
assert service.bot_db == mock_bot_db
|
||||
assert service.settings == mock_settings
|
||||
|
||||
def test_service_attributes(self, voice_service):
|
||||
"""Тест атрибутов сервиса"""
|
||||
assert hasattr(voice_service, 'bot_db')
|
||||
assert hasattr(voice_service, 'settings')
|
||||
assert hasattr(voice_service, 'get_welcome_sticker')
|
||||
assert hasattr(voice_service, 'get_random_audio')
|
||||
assert hasattr(voice_service, 'mark_audio_as_listened')
|
||||
assert hasattr(voice_service, 'clear_user_listenings')
|
||||
assert hasattr(voice_service, 'get_remaining_audio_count')
|
||||
assert hasattr(voice_service, 'send_welcome_messages')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main([__file__])
|
||||
200
tests/test_voice_utils.py
Normal file
200
tests/test_voice_utils.py
Normal file
@@ -0,0 +1,200 @@
|
||||
import pytest
|
||||
from unittest.mock import Mock, patch
|
||||
from datetime import datetime, timedelta
|
||||
from aiogram import types
|
||||
|
||||
from helper_bot.handlers.voice.utils import (
|
||||
get_last_message_text,
|
||||
validate_voice_message,
|
||||
get_user_emoji_safe,
|
||||
format_time_ago,
|
||||
plural_time
|
||||
)
|
||||
|
||||
|
||||
class TestVoiceUtils:
|
||||
"""Тесты для утилит voice модуля"""
|
||||
|
||||
@pytest.fixture
|
||||
def mock_bot_db(self):
|
||||
"""Мок для базы данных"""
|
||||
mock_db = Mock()
|
||||
mock_db.settings = {
|
||||
'Telegram': {
|
||||
'group_for_logs': 'test_logs_chat'
|
||||
}
|
||||
}
|
||||
return mock_db
|
||||
|
||||
@pytest.fixture
|
||||
def mock_message(self):
|
||||
"""Мок для сообщения"""
|
||||
message = Mock(spec=types.Message)
|
||||
message.from_user.id = 123
|
||||
message.from_user.first_name = "Test"
|
||||
message.from_user.full_name = "Test User"
|
||||
message.from_user.username = "testuser"
|
||||
message.from_user.is_bot = False
|
||||
message.from_user.language_code = "ru"
|
||||
message.chat.id = 456
|
||||
return message
|
||||
|
||||
def test_get_last_message_text(self, mock_bot_db):
|
||||
"""Тест получения последнего сообщения"""
|
||||
mock_bot_db.last_date_audio.return_value = "2025-01-01 12:00:00"
|
||||
|
||||
result = get_last_message_text(mock_bot_db)
|
||||
|
||||
assert result is not None
|
||||
assert "минут" in result or "часа" in result or "дня" in result
|
||||
mock_bot_db.last_date_audio.assert_called_once()
|
||||
|
||||
def test_validate_voice_message_valid(self):
|
||||
"""Тест валидации голосового сообщения"""
|
||||
mock_message = Mock()
|
||||
mock_message.content_type = 'voice'
|
||||
mock_message.voice = Mock()
|
||||
|
||||
result = validate_voice_message(mock_message)
|
||||
|
||||
assert result is True
|
||||
|
||||
def test_validate_voice_message_invalid(self):
|
||||
"""Тест валидации невалидного сообщения"""
|
||||
mock_message = Mock()
|
||||
mock_message.voice = None
|
||||
|
||||
result = validate_voice_message(mock_message)
|
||||
|
||||
assert result is False
|
||||
|
||||
def test_get_user_emoji_safe_with_emoji(self, mock_bot_db):
|
||||
"""Тест безопасного получения эмодзи пользователя когда эмодзи есть"""
|
||||
mock_bot_db.check_emoji_for_user.return_value = "😊"
|
||||
|
||||
result = get_user_emoji_safe(mock_bot_db, 123)
|
||||
|
||||
assert result == "😊"
|
||||
mock_bot_db.check_emoji_for_user.assert_called_once_with(123)
|
||||
|
||||
def test_get_user_emoji_safe_without_emoji(self, mock_bot_db):
|
||||
"""Тест безопасного получения эмодзи пользователя когда эмодзи нет"""
|
||||
mock_bot_db.check_emoji_for_user.return_value = None
|
||||
|
||||
result = get_user_emoji_safe(mock_bot_db, 123)
|
||||
|
||||
assert result == "😊"
|
||||
mock_bot_db.check_emoji_for_user.assert_called_once_with(123)
|
||||
|
||||
def test_get_user_emoji_safe_with_empty_emoji(self, mock_bot_db):
|
||||
"""Тест безопасного получения эмодзи пользователя с пустым эмодзи"""
|
||||
mock_bot_db.check_emoji_for_user.return_value = ""
|
||||
|
||||
result = get_user_emoji_safe(mock_bot_db, 123)
|
||||
|
||||
assert result == "😊"
|
||||
mock_bot_db.check_emoji_for_user.assert_called_once_with(123)
|
||||
|
||||
def test_get_user_emoji_safe_with_error(self, mock_bot_db):
|
||||
"""Тест безопасного получения эмодзи пользователя при ошибке"""
|
||||
mock_bot_db.check_emoji_for_user.return_value = "Ошибка"
|
||||
|
||||
result = get_user_emoji_safe(mock_bot_db, 123)
|
||||
|
||||
assert result == "Ошибка"
|
||||
mock_bot_db.check_emoji_for_user.assert_called_once_with(123)
|
||||
|
||||
def test_format_time_ago_minutes(self):
|
||||
"""Тест форматирования времени в минутах"""
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
# Создаем дату 30 минут назад
|
||||
test_date = (datetime.now() - timedelta(minutes=30)).strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
result = format_time_ago(test_date)
|
||||
|
||||
assert result is not None
|
||||
assert "минут" in result
|
||||
assert "30" in result or "29" in result or "31" in result
|
||||
|
||||
def test_format_time_ago_hours(self):
|
||||
"""Тест форматирования времени в часах"""
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
# Создаем дату 2 часа назад
|
||||
test_date = (datetime.now() - timedelta(hours=2)).strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
result = format_time_ago(test_date)
|
||||
|
||||
assert result is not None
|
||||
assert "часа" in result or "часов" in result
|
||||
|
||||
def test_format_time_ago_days(self):
|
||||
"""Тест форматирования времени в днях"""
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
# Создаем дату 3 дня назад
|
||||
test_date = (datetime.now() - timedelta(days=3)).strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
result = format_time_ago(test_date)
|
||||
|
||||
assert result is not None
|
||||
assert "дня" in result or "дней" in result
|
||||
|
||||
def test_format_time_ago_none(self):
|
||||
"""Тест форматирования времени с None"""
|
||||
result = format_time_ago(None)
|
||||
|
||||
assert result is None
|
||||
|
||||
def test_format_time_ago_invalid_format(self):
|
||||
"""Тест форматирования времени с неверным форматом"""
|
||||
result = format_time_ago("invalid_date_format")
|
||||
|
||||
assert result is None
|
||||
|
||||
def test_plural_time_minutes(self):
|
||||
"""Тест множественного числа для минут"""
|
||||
assert "1 минуту" in plural_time(1, 1)
|
||||
assert "2 минуты" in plural_time(1, 2)
|
||||
assert "5 минут" in plural_time(1, 5)
|
||||
assert "11 минут" in plural_time(1, 11)
|
||||
assert "21 минуту" in plural_time(1, 21)
|
||||
|
||||
def test_plural_time_hours(self):
|
||||
"""Тест множественного числа для часов"""
|
||||
assert "1 час" in plural_time(2, 1)
|
||||
assert "2 часа" in plural_time(2, 2)
|
||||
assert "5 часов" in plural_time(2, 5)
|
||||
assert "11 часов" in plural_time(2, 11)
|
||||
assert "21 час" in plural_time(2, 21)
|
||||
|
||||
def test_plural_time_days(self):
|
||||
"""Тест множественного числа для дней"""
|
||||
assert "1 день" in plural_time(3, 1)
|
||||
assert "2 дня" in plural_time(3, 2)
|
||||
assert "5 дней" in plural_time(3, 5)
|
||||
assert "11 дней" in plural_time(3, 11)
|
||||
assert "21 день" in plural_time(3, 21)
|
||||
|
||||
def test_plural_time_invalid_type(self):
|
||||
"""Тест множественного числа с неверным типом"""
|
||||
result = plural_time(4, 5)
|
||||
|
||||
assert result == "5"
|
||||
|
||||
def test_plural_time_edge_cases(self):
|
||||
"""Тест граничных случаев для множественного числа"""
|
||||
# Тест для 0
|
||||
assert "0 минут" in plural_time(1, 0)
|
||||
assert "0 часов" in plural_time(2, 0)
|
||||
assert "0 дней" in plural_time(3, 0)
|
||||
|
||||
# Тест для больших чисел
|
||||
assert "100 минут" in plural_time(1, 100)
|
||||
assert "100 часов" in plural_time(2, 100)
|
||||
assert "100 дней" in plural_time(3, 100)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main([__file__])
|
||||
Reference in New Issue
Block a user