diff --git a/scripts/performance_tests.py b/scripts/performance_tests.py index 69f93a8..1fdd568 100644 --- a/scripts/performance_tests.py +++ b/scripts/performance_tests.py @@ -11,6 +11,7 @@ import sys import time import statistics import hashlib +from typing import Dict, List, Any, Optional, Tuple # Add project root to path sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) @@ -23,7 +24,7 @@ from prettytable import PrettyTable # Django setup - only for local testing -def setup_django(): +def setup_django() -> None: """Setup Django for local testing""" os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ivatar.settings") import django @@ -35,7 +36,7 @@ class PerformanceTestRunner: """Main performance test runner""" # Define all avatar styles and sizes to test - AVATAR_STYLES = [ + AVATAR_STYLES: List[str] = [ "identicon", "monsterid", "robohash", @@ -45,21 +46,21 @@ class PerformanceTestRunner: "mm", "mmng", ] - AVATAR_SIZES = [80, 256] + AVATAR_SIZES: List[int] = [80, 256] def __init__( self, - base_url="http://localhost:8000", - concurrent_users=10, - test_cache=True, - remote_testing=False, - ): - self.base_url = base_url - self.concurrent_users = concurrent_users - self.test_cache = test_cache - self.remote_testing = remote_testing - self.client = None - self.results = {} + base_url: str = "http://localhost:8000", + concurrent_users: int = 10, + test_cache: bool = True, + remote_testing: bool = False, + ) -> None: + self.base_url: str = base_url + self.concurrent_users: int = concurrent_users + self.test_cache: bool = test_cache + self.remote_testing: bool = remote_testing + self.client: Optional[Any] = None # Django test client + self.results: Dict[str, Any] = {} # Determine if we're testing locally or remotely if remote_testing or not base_url.startswith("http://localhost"): @@ -73,7 +74,7 @@ class PerformanceTestRunner: self.client = Client() - def setup_test_data(self): + def setup_test_data(self) -> None: """Create test data for performance tests""" print("Setting up test data...") @@ -97,7 +98,7 @@ class PerformanceTestRunner: print(f"Created {len(test_emails)} test users and emails") - def _generate_test_cases(self): + def _generate_test_cases(self) -> List[Dict[str, Any]]: """Generate test cases for all avatar styles and sizes""" test_cases = [] for style in self.AVATAR_STYLES: @@ -105,7 +106,9 @@ class PerformanceTestRunner: test_cases.append({"default": style, "size": size}) return test_cases - def _test_single_avatar_request(self, case, email, use_requests=False): + def _test_single_avatar_request( + self, case: Dict[str, Any], email: str, use_requests: bool = False + ) -> Dict[str, Any]: """Test a single avatar request - shared logic for local and remote testing""" # Use libravatar library to generate the URL full_url = libravatar_url( @@ -165,13 +168,15 @@ class PerformanceTestRunner: } else: # Local testing with Django test client + if self.client is None: + raise RuntimeError("Django test client not initialized") response = self.client.get(url_path) end_time = time.time() duration = (end_time - start_time) * 1000 # Check for cache information in response headers cache_status = "unknown" - if hasattr(response, "get") and callable(response.get): + if hasattr(response, "get") and callable(getattr(response, "get", None)): cache_control = response.get("Cache-Control", "") age = response.get("Age", "0") if age and int(age) > 0: @@ -192,10 +197,10 @@ class PerformanceTestRunner: "email": email, } - def _display_avatar_results(self, results): + def _display_avatar_results(self, results: List[Dict[str, Any]]) -> None: """Display avatar test results using prettytable for perfect alignment""" # Group results by avatar style - style_results = {} + style_results: Dict[str, List[Dict[str, Any]]] = {} for result in results: style = result["test"].split("_")[0] # Extract style from test name if style not in style_results: @@ -294,7 +299,7 @@ class PerformanceTestRunner: print(table) - def test_avatar_generation_performance(self): + def test_avatar_generation_performance(self) -> None: """Test avatar generation performance""" print("\n=== Avatar Generation Performance Test ===") @@ -346,7 +351,7 @@ class PerformanceTestRunner: "results": results, } - def test_concurrent_load(self): + def test_concurrent_load(self) -> None: """Test concurrent load handling""" print("\n=== Concurrent Load Test ===") @@ -444,7 +449,7 @@ class PerformanceTestRunner: ), } - def _test_remote_concurrent_load(self, num_requests): + def _test_remote_concurrent_load(self, num_requests: int) -> List[Dict[str, Any]]: """Test concurrent load against remote server""" import requests # noqa: F401 from concurrent.futures import ThreadPoolExecutor, as_completed @@ -505,7 +510,7 @@ class PerformanceTestRunner: return results - def _test_local_concurrent_load(self, num_requests): + def _test_local_concurrent_load(self, num_requests: int) -> List[Dict[str, Any]]: """Test concurrent load locally using avatar generation functions""" results = [] @@ -578,7 +583,7 @@ class PerformanceTestRunner: return results - def test_database_performance(self): + def test_database_performance(self) -> None: """Test database query performance""" print("\n=== Database Performance Test ===") @@ -627,7 +632,7 @@ class PerformanceTestRunner: else: print(f" ✅ Database query count is reasonable ({query_count} queries)") - def test_cache_performance(self): + def test_cache_performance(self) -> None: """Test caching effectiveness""" if not self.test_cache: print("\n=== Cache Performance Test ===") @@ -701,7 +706,7 @@ class PerformanceTestRunner: "cache_headers": getattr(self, "cache_info", {}), } - def _test_remote_cache_performance(self, email): + def _test_remote_cache_performance(self, email: str) -> Tuple[float, float]: """Test cache performance against remote server""" import requests @@ -776,7 +781,7 @@ class PerformanceTestRunner: return first_duration, second_duration - def _test_local_cache_performance(self, email): + def _test_local_cache_performance(self, email: str) -> Tuple[float, float]: """Test cache performance locally""" # Use libravatar library to generate the URL full_url = libravatar_url(email=email, size=80, default="identicon") @@ -785,17 +790,19 @@ class PerformanceTestRunner: # First request (cache miss) start_time = time.time() - self.client.get(url_path) + if self.client: + self.client.get(url_path) first_duration = (time.time() - start_time) * 1000 # Second request (should be cache hit) start_time = time.time() - self.client.get(url_path) + if self.client: + self.client.get(url_path) second_duration = (time.time() - start_time) * 1000 return first_duration, second_duration - def run_all_tests(self): + def run_all_tests(self) -> Optional[Dict[str, Any]]: """Run all performance tests""" print("Starting Libravatar Performance Tests") print("=" * 50) @@ -837,7 +844,7 @@ class PerformanceTestRunner: print(f"Performance test failed: {e}") return None - def test_remote_avatar_performance(self): + def test_remote_avatar_performance(self) -> None: """Test avatar generation performance on remote server""" print("\n=== Remote Avatar Performance Test ===") @@ -892,7 +899,7 @@ class PerformanceTestRunner: "success_rate": len(successful_results) / len(results) if results else 0, } - def assess_overall_performance(self): + def assess_overall_performance(self) -> bool: """Provide overall performance assessment""" print("\n=== OVERALL PERFORMANCE ASSESSMENT ===") @@ -937,7 +944,7 @@ class PerformanceTestRunner: return len(warnings) > 0 -def main(): +def main() -> Optional[Dict[str, Any]]: """Main entry point""" import argparse