Files
core/tests/test_redis_functionality.py

304 lines
12 KiB
Python
Raw Normal View History

2025-08-17 11:09:29 +03:00
"""
Качественные тесты функциональности Redis сервиса.
Тестируем реальное поведение, а не просто наличие методов.
"""
import pytest
import asyncio
import json
from services.redis import RedisService
class TestRedisFunctionality:
"""Тесты реальной функциональности Redis"""
@pytest.fixture
async def redis_service(self):
"""Создает тестовый Redis сервис"""
service = RedisService("redis://localhost:6379/1") # Используем БД 1 для тестов
await service.connect()
yield service
await service.disconnect()
@pytest.mark.asyncio
async def test_redis_connection_lifecycle(self, redis_service):
"""Тест жизненного цикла подключения к Redis"""
# Проверяем что подключение активно
assert redis_service.is_connected is True
# Отключаемся
await redis_service.disconnect()
assert redis_service.is_connected is False
# Подключаемся снова
await redis_service.connect()
assert redis_service.is_connected is True
@pytest.mark.asyncio
async def test_redis_basic_operations(self, redis_service):
"""Тест базовых операций Redis"""
# Очищаем тестовую БД
await redis_service.execute("FLUSHDB")
# Тест SET/GET
await redis_service.set("test_key", "test_value")
result = await redis_service.get("test_key")
assert result == "test_value"
# Тест SET с TTL - используем правильный параметр 'ex'
await redis_service.set("test_key_ttl", "test_value_ttl", ex=1)
result = await redis_service.get("test_key_ttl")
assert result == "test_value_ttl"
# Ждем истечения TTL
await asyncio.sleep(1.1)
result = await redis_service.get("test_key_ttl")
assert result is None
# Тест DELETE
await redis_service.set("test_key_delete", "test_value")
await redis_service.delete("test_key_delete")
result = await redis_service.get("test_key_delete")
assert result is None
# Тест EXISTS
await redis_service.set("test_key_exists", "test_value")
exists = await redis_service.exists("test_key_exists")
assert exists is True
exists = await redis_service.exists("non_existent_key")
assert exists is False
@pytest.mark.asyncio
async def test_redis_hash_operations(self, redis_service):
"""Тест операций с хешами Redis"""
# Очищаем тестовую БД
await redis_service.execute("FLUSHDB")
# Тест HSET/HGET
await redis_service.hset("test_hash", "field1", "value1")
await redis_service.hset("test_hash", "field2", "value2")
result = await redis_service.hget("test_hash", "field1")
assert result == "value1"
result = await redis_service.hget("test_hash", "field2")
assert result == "value2"
# Тест HGETALL
all_fields = await redis_service.hgetall("test_hash")
assert all_fields == {"field1": "value1", "field2": "value2"}
@pytest.mark.asyncio
async def test_redis_set_operations(self, redis_service):
"""Тест операций с множествами Redis"""
# Очищаем тестовую БД
await redis_service.execute("FLUSHDB")
# Тест SADD
await redis_service.sadd("test_set", "member1")
await redis_service.sadd("test_set", "member2")
await redis_service.sadd("test_set", "member3")
# Тест SMEMBERS
members = await redis_service.smembers("test_set")
assert len(members) == 3
assert "member1" in members
assert "member2" in members
assert "member3" in members
# Тест SREM
await redis_service.srem("test_set", "member2")
members = await redis_service.smembers("test_set")
assert len(members) == 2
assert "member2" not in members
@pytest.mark.asyncio
async def test_redis_serialization(self, redis_service):
"""Тест сериализации/десериализации данных"""
# Очищаем тестовую БД
await redis_service.execute("FLUSHDB")
# Тест с простыми типами
test_data = {
"string": "test_string",
"number": 42,
"boolean": True,
"list": [1, 2, 3],
"dict": {"nested": "value"}
}
# Сериализуем и сохраняем
await redis_service.serialize_and_set("test_serialization", test_data)
# Получаем и десериализуем
result = await redis_service.get_and_deserialize("test_serialization")
assert result == test_data
# Тест с None
await redis_service.serialize_and_set("test_none", None)
result = await redis_service.get_and_deserialize("test_none")
assert result is None
@pytest.mark.asyncio
async def test_redis_pipeline(self, redis_service):
"""Тест pipeline операций Redis"""
# Очищаем тестовую БД
await redis_service.execute("FLUSHDB")
# Создаем pipeline через правильный метод
pipeline = redis_service.pipeline()
assert pipeline is not None
# Добавляем команды в pipeline
pipeline.set("key1", "value1")
pipeline.set("key2", "value2")
pipeline.set("key3", "value3")
# Выполняем pipeline
results = await pipeline.execute()
# Проверяем результаты
assert len(results) == 3
# Проверяем что данные сохранились
value1 = await redis_service.get("key1")
value2 = await redis_service.get("key2")
value3 = await redis_service.get("key3")
assert value1 == "value1"
assert value2 == "value2"
assert value3 == "value3"
@pytest.mark.asyncio
async def test_redis_publish_subscribe(self, redis_service):
"""Тест pub/sub функциональности Redis"""
# Очищаем тестовую БД
await redis_service.execute("FLUSHDB")
# Создаем список для хранения полученных сообщений
received_messages = []
# Функция для обработки сообщений
async def message_handler(channel, message):
received_messages.append((channel, message))
# Подписываемся на канал - используем правильный способ
# Создаем pubsub объект из клиента
if redis_service._client:
pubsub = redis_service._client.pubsub()
await pubsub.subscribe("test_channel")
# Запускаем прослушивание в фоне
async def listen_messages():
async for message in pubsub.listen():
if message["type"] == "message":
await message_handler(message["channel"], message["data"])
# Запускаем прослушивание
listener_task = asyncio.create_task(listen_messages())
# Ждем немного для установки соединения
await asyncio.sleep(0.1)
# Публикуем сообщение
await redis_service.publish("test_channel", "test_message")
# Ждем получения сообщения
await asyncio.sleep(0.1)
# Останавливаем прослушивание
listener_task.cancel()
await pubsub.unsubscribe("test_channel")
await pubsub.close()
# Проверяем что сообщение получено
assert len(received_messages) > 0
# Проверяем канал и сообщение - учитываем возможные различия в кодировке
channel = received_messages[0][0]
message = received_messages[0][1]
# Канал может быть в байтах или строке
if isinstance(channel, bytes):
channel = channel.decode('utf-8')
assert channel == "test_channel"
# Сообщение может быть в байтах или строке
if isinstance(message, bytes):
message = message.decode('utf-8')
assert message == "test_message"
else:
pytest.skip("Redis client not available")
@pytest.mark.asyncio
async def test_redis_error_handling(self, redis_service):
"""Тест обработки ошибок Redis"""
# Очищаем тестовую БД
await redis_service.execute("FLUSHDB")
# Тест с несуществующей командой
try:
await redis_service.execute("NONEXISTENT_COMMAND")
print("⚠️ Несуществующая команда выполнилась без ошибки")
except Exception as e:
print(f"✅ Ошибка обработана корректно: {e}")
# Тест с неправильными аргументами
try:
await redis_service.execute("SET", "key") # Недостаточно аргументов
print("⚠️ SET с недостаточными аргументами выполнился без ошибки")
except Exception as e:
print(f"✅ Ошибка обработана корректно: {e}")
@pytest.mark.asyncio
async def test_redis_performance(self, redis_service):
"""Тест производительности Redis операций"""
# Очищаем тестовую БД
await redis_service.execute("FLUSHDB")
# Тест массовой записи
start_time = asyncio.get_event_loop().time()
for i in range(100):
await redis_service.set(f"perf_key_{i}", f"perf_value_{i}")
write_time = asyncio.get_event_loop().time() - start_time
# Тест массового чтения
start_time = asyncio.get_event_loop().time()
for i in range(100):
await redis_service.get(f"perf_key_{i}")
read_time = asyncio.get_event_loop().time() - start_time
# Проверяем что операции выполняются достаточно быстро
assert write_time < 1.0 # Запись 100 ключей должна занимать менее 1 секунды
assert read_time < 1.0 # Чтение 100 ключей должно занимать менее 1 секунды
print(f"Write time: {write_time:.3f}s, Read time: {read_time:.3f}s")
@pytest.mark.asyncio
async def test_redis_data_persistence(self, redis_service):
"""Тест персистентности данных Redis"""
# Очищаем тестовую БД
await redis_service.execute("FLUSHDB")
# Сохраняем данные
test_data = {"persistent": "data", "number": 123}
await redis_service.serialize_and_set("persistent_key", test_data)
# Проверяем что данные сохранились
result = await redis_service.get_and_deserialize("persistent_key")
assert result == test_data
# Переподключаемся к Redis
await redis_service.disconnect()
await redis_service.connect()
# Проверяем что данные все еще доступны
result = await redis_service.get_and_deserialize("persistent_key")
assert result == test_data