347 lines
11 KiB
Python
347 lines
11 KiB
Python
"""
|
|
Tests for Redis caching system.
|
|
"""
|
|
|
|
import pytest
|
|
import asyncio
|
|
from datetime import datetime, timezone
|
|
from ..caching.redis_manager import RedisManager
|
|
from ..caching.cache_keys import CacheKeys
|
|
from ..caching.data_serializer import DataSerializer
|
|
from ..models.core import OrderBookSnapshot, HeatmapData, PriceLevel, HeatmapPoint
|
|
|
|
|
|
@pytest.fixture
|
|
async def redis_manager():
|
|
"""Create and initialize Redis manager for testing"""
|
|
manager = RedisManager()
|
|
await manager.initialize()
|
|
yield manager
|
|
await manager.close()
|
|
|
|
|
|
@pytest.fixture
|
|
def cache_keys():
|
|
"""Create cache keys helper"""
|
|
return CacheKeys()
|
|
|
|
|
|
@pytest.fixture
|
|
def data_serializer():
|
|
"""Create data serializer"""
|
|
return DataSerializer()
|
|
|
|
|
|
@pytest.fixture
|
|
def sample_orderbook():
|
|
"""Create sample order book for testing"""
|
|
return OrderBookSnapshot(
|
|
symbol="BTCUSDT",
|
|
exchange="binance",
|
|
timestamp=datetime.now(timezone.utc),
|
|
bids=[
|
|
PriceLevel(price=50000.0, size=1.5),
|
|
PriceLevel(price=49999.0, size=2.0)
|
|
],
|
|
asks=[
|
|
PriceLevel(price=50001.0, size=1.0),
|
|
PriceLevel(price=50002.0, size=1.5)
|
|
]
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def sample_heatmap():
|
|
"""Create sample heatmap for testing"""
|
|
heatmap = HeatmapData(
|
|
symbol="BTCUSDT",
|
|
timestamp=datetime.now(timezone.utc),
|
|
bucket_size=1.0
|
|
)
|
|
|
|
# Add some sample points
|
|
heatmap.data = [
|
|
HeatmapPoint(price=50000.0, volume=1.5, intensity=0.8, side='bid'),
|
|
HeatmapPoint(price=50001.0, volume=1.0, intensity=0.6, side='ask'),
|
|
HeatmapPoint(price=49999.0, volume=2.0, intensity=1.0, side='bid'),
|
|
HeatmapPoint(price=50002.0, volume=1.5, intensity=0.7, side='ask')
|
|
]
|
|
|
|
return heatmap
|
|
|
|
|
|
class TestCacheKeys:
|
|
"""Test cases for CacheKeys"""
|
|
|
|
def test_orderbook_key_generation(self, cache_keys):
|
|
"""Test order book key generation"""
|
|
key = cache_keys.orderbook_key("BTCUSDT", "binance")
|
|
assert key == "ob:binance:BTCUSDT"
|
|
|
|
def test_heatmap_key_generation(self, cache_keys):
|
|
"""Test heatmap key generation"""
|
|
# Exchange-specific heatmap
|
|
key1 = cache_keys.heatmap_key("BTCUSDT", 1.0, "binance")
|
|
assert key1 == "hm:binance:BTCUSDT:1.0"
|
|
|
|
# Consolidated heatmap
|
|
key2 = cache_keys.heatmap_key("BTCUSDT", 1.0)
|
|
assert key2 == "hm:consolidated:BTCUSDT:1.0"
|
|
|
|
def test_ttl_determination(self, cache_keys):
|
|
"""Test TTL determination for different key types"""
|
|
ob_key = cache_keys.orderbook_key("BTCUSDT", "binance")
|
|
hm_key = cache_keys.heatmap_key("BTCUSDT", 1.0)
|
|
|
|
assert cache_keys.get_ttl(ob_key) == cache_keys.ORDERBOOK_TTL
|
|
assert cache_keys.get_ttl(hm_key) == cache_keys.HEATMAP_TTL
|
|
|
|
def test_key_parsing(self, cache_keys):
|
|
"""Test cache key parsing"""
|
|
ob_key = cache_keys.orderbook_key("BTCUSDT", "binance")
|
|
parsed = cache_keys.parse_key(ob_key)
|
|
|
|
assert parsed['type'] == 'orderbook'
|
|
assert parsed['exchange'] == 'binance'
|
|
assert parsed['symbol'] == 'BTCUSDT'
|
|
|
|
|
|
class TestDataSerializer:
|
|
"""Test cases for DataSerializer"""
|
|
|
|
def test_simple_data_serialization(self, data_serializer):
|
|
"""Test serialization of simple data types"""
|
|
test_data = {
|
|
'string': 'test',
|
|
'number': 42,
|
|
'float': 3.14,
|
|
'boolean': True,
|
|
'list': [1, 2, 3],
|
|
'nested': {'key': 'value'}
|
|
}
|
|
|
|
# Serialize and deserialize
|
|
serialized = data_serializer.serialize(test_data)
|
|
deserialized = data_serializer.deserialize(serialized)
|
|
|
|
assert deserialized == test_data
|
|
|
|
def test_orderbook_serialization(self, data_serializer, sample_orderbook):
|
|
"""Test order book serialization"""
|
|
# Serialize and deserialize
|
|
serialized = data_serializer.serialize(sample_orderbook)
|
|
deserialized = data_serializer.deserialize(serialized)
|
|
|
|
assert isinstance(deserialized, OrderBookSnapshot)
|
|
assert deserialized.symbol == sample_orderbook.symbol
|
|
assert deserialized.exchange == sample_orderbook.exchange
|
|
assert len(deserialized.bids) == len(sample_orderbook.bids)
|
|
assert len(deserialized.asks) == len(sample_orderbook.asks)
|
|
|
|
def test_heatmap_serialization(self, data_serializer, sample_heatmap):
|
|
"""Test heatmap serialization"""
|
|
# Test specialized heatmap serialization
|
|
serialized = data_serializer.serialize_heatmap(sample_heatmap)
|
|
deserialized = data_serializer.deserialize_heatmap(serialized)
|
|
|
|
assert isinstance(deserialized, HeatmapData)
|
|
assert deserialized.symbol == sample_heatmap.symbol
|
|
assert deserialized.bucket_size == sample_heatmap.bucket_size
|
|
assert len(deserialized.data) == len(sample_heatmap.data)
|
|
|
|
# Check first point
|
|
original_point = sample_heatmap.data[0]
|
|
deserialized_point = deserialized.data[0]
|
|
assert deserialized_point.price == original_point.price
|
|
assert deserialized_point.volume == original_point.volume
|
|
assert deserialized_point.side == original_point.side
|
|
|
|
|
|
class TestRedisManager:
|
|
"""Test cases for RedisManager"""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_basic_set_get(self, redis_manager):
|
|
"""Test basic set and get operations"""
|
|
# Set a simple value
|
|
key = "test:basic"
|
|
value = {"test": "data", "number": 42}
|
|
|
|
success = await redis_manager.set(key, value, ttl=60)
|
|
assert success is True
|
|
|
|
# Get the value back
|
|
retrieved = await redis_manager.get(key)
|
|
assert retrieved == value
|
|
|
|
# Clean up
|
|
await redis_manager.delete(key)
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_orderbook_caching(self, redis_manager, sample_orderbook):
|
|
"""Test order book caching"""
|
|
# Cache order book
|
|
success = await redis_manager.cache_orderbook(sample_orderbook)
|
|
assert success is True
|
|
|
|
# Retrieve order book
|
|
retrieved = await redis_manager.get_orderbook(
|
|
sample_orderbook.symbol,
|
|
sample_orderbook.exchange
|
|
)
|
|
|
|
assert retrieved is not None
|
|
assert isinstance(retrieved, OrderBookSnapshot)
|
|
assert retrieved.symbol == sample_orderbook.symbol
|
|
assert retrieved.exchange == sample_orderbook.exchange
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_heatmap_caching(self, redis_manager, sample_heatmap):
|
|
"""Test heatmap caching"""
|
|
# Cache heatmap
|
|
success = await redis_manager.set_heatmap(
|
|
sample_heatmap.symbol,
|
|
sample_heatmap,
|
|
exchange="binance"
|
|
)
|
|
assert success is True
|
|
|
|
# Retrieve heatmap
|
|
retrieved = await redis_manager.get_heatmap(
|
|
sample_heatmap.symbol,
|
|
exchange="binance"
|
|
)
|
|
|
|
assert retrieved is not None
|
|
assert isinstance(retrieved, HeatmapData)
|
|
assert retrieved.symbol == sample_heatmap.symbol
|
|
assert len(retrieved.data) == len(sample_heatmap.data)
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_multi_operations(self, redis_manager):
|
|
"""Test multi-get and multi-set operations"""
|
|
# Prepare test data
|
|
test_data = {
|
|
"test:multi1": {"value": 1},
|
|
"test:multi2": {"value": 2},
|
|
"test:multi3": {"value": 3}
|
|
}
|
|
|
|
# Multi-set
|
|
success = await redis_manager.mset(test_data, ttl=60)
|
|
assert success is True
|
|
|
|
# Multi-get
|
|
keys = list(test_data.keys())
|
|
values = await redis_manager.mget(keys)
|
|
|
|
assert len(values) == 3
|
|
assert all(v is not None for v in values)
|
|
|
|
# Verify values
|
|
for i, key in enumerate(keys):
|
|
assert values[i] == test_data[key]
|
|
|
|
# Clean up
|
|
for key in keys:
|
|
await redis_manager.delete(key)
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_key_expiration(self, redis_manager):
|
|
"""Test key expiration"""
|
|
key = "test:expiration"
|
|
value = {"expires": "soon"}
|
|
|
|
# Set with short TTL
|
|
success = await redis_manager.set(key, value, ttl=1)
|
|
assert success is True
|
|
|
|
# Should exist immediately
|
|
exists = await redis_manager.exists(key)
|
|
assert exists is True
|
|
|
|
# Wait for expiration
|
|
await asyncio.sleep(2)
|
|
|
|
# Should not exist after expiration
|
|
exists = await redis_manager.exists(key)
|
|
assert exists is False
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_cache_miss(self, redis_manager):
|
|
"""Test cache miss behavior"""
|
|
# Try to get non-existent key
|
|
value = await redis_manager.get("test:nonexistent")
|
|
assert value is None
|
|
|
|
# Check statistics
|
|
stats = redis_manager.get_stats()
|
|
assert stats['misses'] > 0
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_health_check(self, redis_manager):
|
|
"""Test Redis health check"""
|
|
health = await redis_manager.health_check()
|
|
|
|
assert isinstance(health, dict)
|
|
assert 'redis_ping' in health
|
|
assert 'total_keys' in health
|
|
assert 'hit_rate' in health
|
|
|
|
# Should be able to ping
|
|
assert health['redis_ping'] is True
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_statistics_tracking(self, redis_manager):
|
|
"""Test statistics tracking"""
|
|
# Reset stats
|
|
redis_manager.reset_stats()
|
|
|
|
# Perform some operations
|
|
await redis_manager.set("test:stats1", {"data": 1})
|
|
await redis_manager.set("test:stats2", {"data": 2})
|
|
await redis_manager.get("test:stats1")
|
|
await redis_manager.get("test:nonexistent")
|
|
|
|
# Check statistics
|
|
stats = redis_manager.get_stats()
|
|
|
|
assert stats['sets'] >= 2
|
|
assert stats['gets'] >= 2
|
|
assert stats['hits'] >= 1
|
|
assert stats['misses'] >= 1
|
|
assert stats['total_operations'] >= 4
|
|
|
|
# Clean up
|
|
await redis_manager.delete("test:stats1")
|
|
await redis_manager.delete("test:stats2")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
# Run simple tests
|
|
async def simple_test():
|
|
manager = RedisManager()
|
|
await manager.initialize()
|
|
|
|
# Test basic operations
|
|
success = await manager.set("test", {"simple": "test"}, ttl=60)
|
|
print(f"Set operation: {'SUCCESS' if success else 'FAILED'}")
|
|
|
|
value = await manager.get("test")
|
|
print(f"Get operation: {'SUCCESS' if value else 'FAILED'}")
|
|
|
|
# Test ping
|
|
ping_result = await manager.ping()
|
|
print(f"Ping test: {'SUCCESS' if ping_result else 'FAILED'}")
|
|
|
|
# Get statistics
|
|
stats = manager.get_stats()
|
|
print(f"Statistics: {stats}")
|
|
|
|
# Clean up
|
|
await manager.delete("test")
|
|
await manager.close()
|
|
|
|
print("Simple Redis test completed")
|
|
|
|
asyncio.run(simple_test()) |