diegobeyl/backtesting
2
1"""2Test suite para Fase 4: Cache Manager3=====================================4Pruebas completas del sistema de caché:5- Memory cache6- Disk cache7- Cache manager multi-nivel8- TTL y expiración9- Estadísticas10"""11 12import sys13import time14import json15from pathlib import Path16 17# Asegurar que el módulo pueda importarse18sys.path.insert(0, "/Users/javier/Projects/backtesting_app_v2")19 20from backtesting_app.core.cache_manager import (21 CacheManager,22 MemoryCache,23 DiskCache,24 CacheEntry,25)26 27 28def test_1_memory_cache_basic():29 """Test 1: Operaciones básicas de memory cache"""30 print("\n✅ Test 1: Memory Cache - Operaciones básicas")31 32 cache = MemoryCache(max_size_mb=10)33 34 # Guardar35 cache.set("key1", {"data": "value1"})36 assert cache.get("key1") == {"data": "value1"}, "No se guardó/recuperó correctamente"37 38 # Borrar39 cache.delete("key1")40 assert cache.get("key1") is None, "No se borró correctamente"41 42 print(" ✅ PASS - Operaciones básicas funcionan")43 44 45def test_2_memory_cache_ttl():46 """Test 2: Memory Cache - Expiración TTL"""47 print("\n✅ Test 2: Memory Cache - TTL y expiración")48 49 cache = MemoryCache(max_size_mb=10)50 51 # Guardar con TTL de 1 segundo52 cache.set("key_ttl", {"data": "expires"}, ttl=1)53 assert cache.get("key_ttl") is not None, "Debería existir antes de expirar"54 55 # Esperar a que expire56 time.sleep(1.1)57 assert cache.get("key_ttl") is None, "Debería haber expirado"58 59 print(" ✅ PASS - TTL funciona correctamente")60 61 62def test_3_disk_cache():63 """Test 3: Disk Cache - Guardado y recuperación"""64 print("\n✅ Test 3: Disk Cache - Guardado en disco")65 66 cache = DiskCache(cache_dir="/tmp/test_cache_disk")67 68 # Guardar69 test_data = {"symbol": "BTC", "price": 45000}70 cache.set("btc_price", test_data)71 72 # Recuperar73 result = cache.get("btc_price")74 assert result is not None, "No se recuperó del disco"75 value, is_valid = result76 assert value == test_data, "Los datos no coinciden"77 assert is_valid, "Los datos no son válidos"78 79 # Limpiar80 cache.clear()81 print(" ✅ PASS - Disk cache funciona")82 83 84def test_4_disk_cache_ttl():85 """Test 4: Disk Cache - TTL en disco"""86 print("\n✅ Test 4: Disk Cache - TTL en disco")87 88 cache = DiskCache(cache_dir="/tmp/test_cache_disk")89 90 # Guardar con TTL91 cache.set("expires_soon", {"value": 123}, ttl=1)92 93 # Antes de expirar94 result = cache.get("expires_soon")95 assert result is not None, "Debería existir"96 97 # Esperar expiración98 time.sleep(1.1)99 result = cache.get("expires_soon")100 assert result is None, "Debería haber expirado"101 102 cache.clear()103 print(" ✅ PASS - TTL en disco funciona")104 105 106def test_5_cache_manager_multi_level():107 """Test 5: Cache Manager - Multi-nivel (memoria + disco)"""108 print("\n✅ Test 5: Cache Manager - Multi-nivel")109 110 cache = CacheManager(memory_size_mb=10, disk_cache_dir="/tmp/test_cache_manager")111 112 # Guardar datos113 data = {"symbol": "ETH", "candles": 100}114 cache.set("eth_data", data, ttl=3600)115 116 # Recuperar de memoria (rápido)117 result = cache.get("eth_data")118 assert result == data, "No recuperó de memoria"119 assert cache.stats_hits == 1, "No registró hit"120 121 # Simular pérdida de memoria (limpiar)122 cache.memory.clear()123 124 # Recuperar de disco (más lento pero existe)125 result = cache.get("eth_data")126 assert result == data, "No recuperó del disco"127 assert cache.stats_hits == 2, "No registró segundo hit"128 129 cache.clear()130 print(" ✅ PASS - Multi-nivel funciona correctamente")131 132 133def test_6_cache_stats():134 """Test 6: Estadísticas del caché"""135 print("\n✅ Test 6: Estadísticas del caché")136 137 cache = CacheManager(memory_size_mb=10)138 139 # Agregar entradas140 for i in range(5):141 cache.set(f"key_{i}", {"value": i * 100})142 143 # Hacer algunos hits144 for i in range(3):145 cache.get(f"key_{i}")146 147 stats = cache.get_stats()148 149 assert stats["memory"]["entries"] == 5, "Número de entradas incorrecto"150 assert stats["stats"]["total_hits"] == 3, "Total de hits incorrecto"151 # Hit rate: 3 hits / (3 hits + 2 misses) = 60%152 assert stats["stats"]["hit_rate_percent"] > 50, "Hit rate debería ser > 50%"153 154 cache.clear()155 print(" ✅ PASS - Estadísticas correctas")156 157 158def test_7_cache_invalidation_pattern():159 """Test 7: Invalidación por patrón"""160 print("\n✅ Test 7: Invalidación por patrón")161 162 cache = CacheManager(memory_size_mb=10)163 164 # Agregar datos solo en memoria (evitar persistencia en disco)165 cache.memory.set("BTC-USDT_1h", {"data": 1})166 cache.memory.set("BTC-USDT_4h", {"data": 2})167 cache.memory.set("ETH-USDT_1h", {"data": 3})168 169 # Invalidar patrón BTC-*170 cache.invalidate_pattern("BTC-*")171 172 assert cache.memory.get("BTC-USDT_1h") is None, "No se invalidó BTC-USDT_1h"173 assert cache.memory.get("BTC-USDT_4h") is None, "No se invalidó BTC-USDT_4h"174 assert cache.memory.get("ETH-USDT_1h") is not None, "Se invalidó ETH-USDT_1h incorrectamente"175 176 cache.clear()177 print(" ✅ PASS - Invalidación por patrón funciona")178 179 180def main():181 """Ejecutar todos los tests"""182 print("=" * 70)183 print("🧪 FASE 4: CACHE MANAGER - TEST SUITE")184 print("=" * 70)185 186 tests = [187 test_1_memory_cache_basic,188 test_2_memory_cache_ttl,189 test_3_disk_cache,190 test_4_disk_cache_ttl,191 test_5_cache_manager_multi_level,192 test_6_cache_stats,193 test_7_cache_invalidation_pattern,194 ]195 196 passed = 0197 failed = 0198 199 for test_func in tests:200 try:201 test_func()202 passed += 1203 except AssertionError as e:204 print(f" ❌ FAIL - {e}")205 failed += 1206 except Exception as e:207 print(f" ❌ ERROR - {e}")208 failed += 1209 210 print("\n" + "=" * 70)211 print(f"📊 RESULTADOS: {passed} PASADAS ✅ | {failed} FALLADAS ❌")212 print("=" * 70)213 214 if failed == 0:215 print("\n🎉 ¡TODOS LOS TESTS PASARON!")216 return 0217 else:218 print(f"\n⚠️ {failed} test(s) fallaron")219 return 1220 221 222if __name__ == "__main__":223 sys.exit(main())224 