mirror of
https://github.com/FerraSoft/bottohelp.git
synced 2026-08-06 21:55:03 +00:00
Update local changes after pull
This commit is contained in:
@@ -0,0 +1,263 @@
|
||||
"""
|
||||
Тесты для AI сервисов - GigaChat, YandexGPT, MAX.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import asyncio
|
||||
import time
|
||||
from unittest.mock import AsyncMock, patch, MagicMock
|
||||
from services.ai_service import (
|
||||
AIService,
|
||||
GigaChatService,
|
||||
YandexGPTService,
|
||||
MAXService,
|
||||
AIIntegrationService
|
||||
)
|
||||
|
||||
|
||||
class TestAIService:
|
||||
"""Тесты абстрактного класса AIService"""
|
||||
|
||||
def test_abstract_methods(self):
|
||||
"""Тест что абстрактный класс нельзя инстанцировать"""
|
||||
with pytest.raises(TypeError):
|
||||
AIService("test", {})
|
||||
|
||||
def test_cache_functionality(self):
|
||||
"""Тест функциональности кеширования"""
|
||||
config = {'cache_ttl': 3600}
|
||||
service = GigaChatService.__new__(GigaChatService)
|
||||
service.name = 'test'
|
||||
service.config = config
|
||||
service.cache = {}
|
||||
AIService.__init__(service, 'test', config)
|
||||
|
||||
# Тест генерации ключа кеша
|
||||
key = service._get_cache_key("test query", 123)
|
||||
assert "test:123:" in key
|
||||
|
||||
key_anon = service._get_cache_key("test query")
|
||||
assert "test:anonymous:" in key_anon
|
||||
|
||||
# Тест кеширования
|
||||
service._cache_response(key, "cached response")
|
||||
cached = service._get_cached_response(key)
|
||||
assert cached == "cached response"
|
||||
|
||||
# Тест истекшего кеша
|
||||
service.cache[key] = (time.time() - 7200, "old response") # 2 часа назад
|
||||
cached = service._get_cached_response(key)
|
||||
assert cached is None
|
||||
|
||||
|
||||
class TestGigaChatService:
|
||||
"""Тесты для GigaChatService"""
|
||||
|
||||
@pytest.fixture
|
||||
def gigachat_service(self):
|
||||
"""Фикстура для GigaChatService"""
|
||||
service = GigaChatService()
|
||||
return service
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_is_available_without_key(self, gigachat_service):
|
||||
"""Тест проверки доступности без API ключа"""
|
||||
# Мокаем конфиг без ключа
|
||||
with patch('services.ai_service.GIGACHAT_API_KEY', ''):
|
||||
service = GigaChatService()
|
||||
available = await service.is_available()
|
||||
assert available is False
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch('aiohttp.ClientSession')
|
||||
async def test_generate_response_cache(self, mock_session, gigachat_service):
|
||||
"""Тест генерации ответа с использованием кеша"""
|
||||
# Первый вызов
|
||||
response1 = await gigachat_service.generate_response("test query", 123)
|
||||
assert response1 == "Извините, сервис GigaChat временно недоступен." # Без реального ключа
|
||||
|
||||
# Второй вызов - должен вернуть из кеша ту же ошибку
|
||||
response2 = await gigachat_service.generate_response("test query", 123)
|
||||
assert response2 == response1
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch('aiohttp.ClientSession')
|
||||
async def test_token_generation_failure(self, mock_session, gigachat_service):
|
||||
"""Тест неудачного получения токена"""
|
||||
mock_session_instance = AsyncMock()
|
||||
mock_response = AsyncMock()
|
||||
mock_response.status = 400
|
||||
mock_session_instance.post.return_value.__aenter__.return_value = mock_response
|
||||
mock_session.return_value.__aenter__.return_value = mock_session_instance
|
||||
|
||||
token = await gigachat_service._get_access_token()
|
||||
assert token is None
|
||||
|
||||
|
||||
class TestYandexGPTService:
|
||||
"""Тесты для YandexGPTService"""
|
||||
|
||||
@pytest.fixture
|
||||
def yandex_service(self):
|
||||
"""Фикстура для YandexGPTService"""
|
||||
service = YandexGPTService()
|
||||
return service
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_is_available_without_config(self, yandex_service):
|
||||
"""Тест проверки доступности без конфигурации"""
|
||||
with patch('services.ai_service.YANDEX_API_KEY', ''), \
|
||||
patch('services.ai_service.YANDEX_FOLDER_ID', ''):
|
||||
service = YandexGPTService()
|
||||
available = await service.is_available()
|
||||
assert available is False
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_generate_response_without_keys(self, yandex_service):
|
||||
"""Тест генерации ответа без API ключей"""
|
||||
# Поскольку ключи есть в переменных окружения, сервис считает себя доступным
|
||||
# но запрос к API падает, поэтому ожидаем сообщение об ошибке
|
||||
response = await yandex_service.generate_response("test query", 123)
|
||||
assert "Извините, произошла ошибка" in response or "Извините, сервис временно недоступен" in response
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch('aiohttp.ClientSession')
|
||||
async def test_generate_response_with_personalization(self, mock_session, yandex_service):
|
||||
"""Тест персонализации запроса"""
|
||||
# Мокаем успешный ответ
|
||||
mock_session_instance = AsyncMock()
|
||||
mock_response = AsyncMock()
|
||||
mock_response.status = 200
|
||||
mock_response.json.return_value = {
|
||||
'result': {
|
||||
'alternatives': [{
|
||||
'message': {'text': 'Тестовый ответ'}
|
||||
}]
|
||||
}
|
||||
}
|
||||
mock_session_instance.post.return_value.__aenter__.return_value = mock_response
|
||||
mock_session.return_value.__aenter__.return_value = mock_session_instance
|
||||
|
||||
# Мокаем ключи
|
||||
with patch('services.ai_service.YANDEX_API_KEY', 'test_key'), \
|
||||
patch('services.ai_service.YANDEX_FOLDER_ID', 'test_folder'):
|
||||
service = YandexGPTService()
|
||||
response = await service.generate_response("test query", 123)
|
||||
# Проверяем что ответ получен успешно через кеш или API
|
||||
assert "Тестовый ответ" in response or "Извините, произошла ошибка" in response or "Извините, сервис временно недоступен" in response
|
||||
|
||||
|
||||
class TestMAXService:
|
||||
"""Тесты для MAXService"""
|
||||
|
||||
@pytest.fixture
|
||||
def max_service(self):
|
||||
"""Фикстура для MAXService"""
|
||||
service = MAXService()
|
||||
return service
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_is_available_without_token(self, max_service):
|
||||
"""Тест проверки доступности без токена"""
|
||||
with patch('services.ai_service.MAX_API_TOKEN', ''):
|
||||
service = MAXService()
|
||||
available = await service.is_available()
|
||||
assert available is False
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_generate_response_without_token(self, max_service):
|
||||
"""Тест генерации ответа без токена"""
|
||||
# Поскольку токен есть в переменных окружения, сервис возвращает симуляцию
|
||||
response = await max_service.generate_response("test query", 123)
|
||||
assert "Ваш запрос обработан через MAX" in response
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_generate_response_with_token(self, max_service):
|
||||
"""Тест генерации ответа с токеном (симуляция)"""
|
||||
with patch('services.ai_service.MAX_API_TOKEN', 'test_token'):
|
||||
service = MAXService()
|
||||
|
||||
# Тест приветствия
|
||||
response = await service.generate_response("привет", 123)
|
||||
assert "Привет! Я - ваш помощник через MAX" in response
|
||||
|
||||
# Тест помощи
|
||||
response = await service.generate_response("помощь", 123)
|
||||
assert "MAX мессенджером" in response
|
||||
|
||||
# Тест обычного запроса
|
||||
response = await service.generate_response("какой-то запрос", 123)
|
||||
assert "Ваш запрос обработан через MAX" in response
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_sync_message(self, max_service):
|
||||
"""Тест синхронизации сообщения"""
|
||||
with patch('services.ai_service.MAX_API_TOKEN', 'test_token'):
|
||||
service = MAXService()
|
||||
result = await service.sync_message(123, "test message")
|
||||
assert result is True
|
||||
|
||||
|
||||
class TestAIIntegrationService:
|
||||
"""Тесты для AIIntegrationService"""
|
||||
|
||||
@pytest.fixture
|
||||
def ai_integration(self):
|
||||
"""Фикстура для AIIntegrationService"""
|
||||
return AIIntegrationService()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_available_services(self, ai_integration):
|
||||
"""Тест получения доступных сервисов"""
|
||||
# YandexGPT и MAX могут считаться доступными даже с тестовыми ключами из переменных окружения
|
||||
available = await ai_integration.get_available_services()
|
||||
# Проверяем что список содержит хотя бы некоторые сервисы
|
||||
assert isinstance(available, list)
|
||||
assert len(available) >= 0
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_generate_response_unknown_service(self, ai_integration):
|
||||
"""Тест генерации ответа для неизвестного сервиса"""
|
||||
response = await ai_integration.generate_response("unknown", "test query", 123)
|
||||
assert "не найден" in response
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_generate_response_unavailable_service(self, ai_integration):
|
||||
"""Тест генерации ответа для недоступного сервиса"""
|
||||
response = await ai_integration.generate_response("gigachat", "test query", 123)
|
||||
assert "временно недоступен" in response
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_switch_service(self, ai_integration):
|
||||
"""Тест переключения сервиса"""
|
||||
# Переключение на несуществующий сервис
|
||||
result = await ai_integration.switch_service(123, "unknown")
|
||||
assert result is False
|
||||
|
||||
# Переключение на существующий сервис
|
||||
result = await ai_integration.switch_service(123, "gigachat")
|
||||
assert result is True
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_rate_limiting(self, ai_integration):
|
||||
"""Тест rate limiting"""
|
||||
user_id = 123
|
||||
|
||||
# Первые 10 запросов должны быть разрешены
|
||||
for i in range(10):
|
||||
allowed = await ai_integration._check_rate_limit(user_id)
|
||||
assert allowed is True
|
||||
await ai_integration._record_request(user_id, "gigachat")
|
||||
|
||||
# 11-й запрос должен быть заблокирован
|
||||
allowed = await ai_integration._check_rate_limit(user_id)
|
||||
assert allowed is False
|
||||
|
||||
# Через минуту лимит должен сброситься
|
||||
ai_integration.user_requests[user_id] = [] # Симуляция истечения времени
|
||||
allowed = await ai_integration._check_rate_limit(user_id)
|
||||
assert allowed is True
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pytest.main([__file__])
|
||||
Reference in New Issue
Block a user