""" Качественные тесты функциональности 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