""" Test Redis cache integration for position ratings. Tests the complete flow: 1. Connect to Redis 2. Fetch ratings from API (cache miss) 3. Verify cached in Redis 4. Fetch again (cache hit) 5. Measure performance improvement 6. Test cache clearing Author: Claude Date: 2025-11-03 Phase: 3E-Final """ import asyncio import pendulum from app.services import redis_client, position_rating_service async def main(): print("=" * 60) print("Redis Cache Integration Test") print("=" * 60) # Step 1: Connect to Redis print("\n1. Connecting to Redis...") try: await redis_client.connect("redis://localhost:6379/0") print(f" ✅ Connected to Redis") # Test ping is_alive = await redis_client.ping() print(f" ✅ Redis ping: {is_alive}") except Exception as e: print(f" ❌ Failed to connect: {e}") return # Step 2: Clear any existing cache print("\n2. Clearing existing cache...") await position_rating_service.clear_cache() print(" ✅ Cache cleared") # Step 3: Fetch ratings from API (should be cache miss) print("\n3. First fetch (API call - cache miss)...") card_id = 8807 # Test player with 7 positions start = pendulum.now('UTC') ratings = await position_rating_service.get_ratings_for_card( card_id=card_id, league_id="pd" ) api_duration = (pendulum.now('UTC') - start).total_seconds() print(f" ✅ Fetched {len(ratings)} ratings in {api_duration:.4f}s") if ratings: print(f" 📋 Positions found:") for rating in ratings: print(f" - {rating.position}: range={rating.range}, error={rating.error}, innings={rating.innings}") # Step 4: Fetch again (should be cache hit from Redis) print("\n4. Second fetch (Redis cache hit)...") start = pendulum.now('UTC') cached_ratings = await position_rating_service.get_ratings_for_card( card_id=card_id, league_id="pd" ) cache_duration = (pendulum.now('UTC') - start).total_seconds() print(f" ✅ Fetched {len(cached_ratings)} ratings in {cache_duration:.6f}s") # Step 5: Calculate performance improvement print("\n5. Performance Comparison:") if cache_duration > 0: speedup = api_duration / cache_duration print(f" API call: {api_duration:.4f}s") print(f" Cache hit: {cache_duration:.6f}s") print(f" ⚡ Speedup: {speedup:.0f}x faster") else: print(f" API call: {api_duration:.4f}s") print(f" Cache hit: < 0.000001s") print(f" ⚡ Speedup: > 100,000x faster") # Step 6: Verify data matches print("\n6. Data Integrity Check:") if len(ratings) == len(cached_ratings): print(f" ✅ Same number of ratings ({len(ratings)})") # Compare each rating matches = 0 for i, (r1, r2) in enumerate(zip(ratings, cached_ratings)): if (r1.position == r2.position and r1.range == r2.range and r1.error == r2.error): matches += 1 if matches == len(ratings): print(f" ✅ All {matches} ratings match exactly") else: print(f" ⚠️ Only {matches}/{len(ratings)} ratings match") else: print(f" ❌ Different counts: API={len(ratings)}, Cache={len(cached_ratings)}") # Step 7: Test single position lookup print("\n7. Single Position Lookup:") rating_ss = await position_rating_service.get_rating_for_position( card_id=card_id, position="SS", league_id="pd" ) if rating_ss: print(f" ✅ Found SS rating: range={rating_ss.range}, error={rating_ss.error}") else: print(f" ❌ SS rating not found") # Step 8: Test cache clearing for specific card print("\n8. Clear cache for specific card...") await position_rating_service.clear_cache(card_id=card_id) print(f" ✅ Cleared cache for card {card_id}") # Verify it's cleared (should be API call again) print("\n9. Verify cache cleared (should be API call)...") start = pendulum.now('UTC') ratings_after_clear = await position_rating_service.get_ratings_for_card( card_id=card_id, league_id="pd" ) duration_after_clear = (pendulum.now('UTC') - start).total_seconds() if duration_after_clear > 0.01: # If it took more than 10ms, likely API call print(f" ✅ Cache was cleared (took {duration_after_clear:.4f}s - API call)") else: print(f" ⚠️ Unexpectedly fast ({duration_after_clear:.6f}s)") # Step 10: Disconnect print("\n10. Disconnecting from Redis...") await redis_client.disconnect() print(" ✅ Disconnected") print("\n" + "=" * 60) print("✅ All tests completed successfully!") print("=" * 60) if __name__ == "__main__": asyncio.run(main())