55import subprocess
66import threading
77import time
8+ from typing import Any , Dict , List , Optional , cast
89
910import pytest
1011import requests
1112
1213
13- def get_namespace ():
14+ def get_namespace () -> str :
1415 return os .environ .get ("NAMESPACE" , "eoapi" )
1516
1617
17- def get_release_name ():
18+ def get_release_name () -> str :
1819 return os .environ .get ("RELEASE_NAME" , "eoapi" )
1920
2021
21- def get_base_url ():
22+ def get_base_url () -> str :
2223 namespace = get_namespace ()
2324
2425 # Check if we have an ingress
@@ -48,7 +49,12 @@ def get_base_url():
4849 return "http://localhost:8080"
4950
5051
51- def kubectl_get (resource , namespace = None , label_selector = None , output = "json" ):
52+ def kubectl_get (
53+ resource : str ,
54+ namespace : Optional [str ] = None ,
55+ label_selector : Optional [str ] = None ,
56+ output : str = "json" ,
57+ ) -> subprocess .CompletedProcess [str ]:
5258 cmd = ["kubectl" , "get" , resource ]
5359
5460 if namespace :
@@ -64,7 +70,7 @@ def kubectl_get(resource, namespace=None, label_selector=None, output="json"):
6470 return result
6571
6672
67- def get_pod_metrics (namespace , service_name ) :
73+ def get_pod_metrics (namespace : str , service_name : str ) -> List [ Dict [ str , str ]] :
6874 release_name = get_release_name ()
6975 result = subprocess .run (
7076 [
@@ -84,7 +90,7 @@ def get_pod_metrics(namespace, service_name):
8490 if result .returncode != 0 :
8591 return []
8692
87- metrics = []
93+ metrics : List [ Dict [ str , str ]] = []
8894 for line in result .stdout .strip ().split ("\n " ):
8995 if line .strip ():
9096 parts = line .split ()
@@ -97,20 +103,21 @@ def get_pod_metrics(namespace, service_name):
97103 return metrics
98104
99105
100- def get_hpa_status (namespace , hpa_name ):
106+ def get_hpa_status (namespace : str , hpa_name : str ) -> Optional [Dict [str , Any ]]:
107+ """Get HPA status for a specific HPA."""
101108 result = kubectl_get ("hpa" , namespace = namespace , output = "json" )
102109 if result .returncode != 0 :
103110 return None
104111
105112 hpas = json .loads (result .stdout )
106113 for hpa in hpas ["items" ]:
107114 if hpa ["metadata" ]["name" ] == hpa_name :
108- return hpa
115+ return cast ( Dict [ str , Any ], hpa )
109116
110117 return None
111118
112119
113- def get_pod_count (namespace , service_name ) :
120+ def get_pod_count (namespace : str , service_name : str ) -> int :
114121 release_name = get_release_name ()
115122 result = kubectl_get (
116123 "pods" ,
@@ -129,7 +136,7 @@ def get_pod_count(namespace, service_name):
129136 return len (running_pods )
130137
131138
132- def make_request (url , timeout = 10 ):
139+ def make_request (url : str , timeout : int = 10 ) -> bool :
133140 """Make a single HTTP request and return success status."""
134141 try :
135142 response = requests .get (url , timeout = timeout )
@@ -139,14 +146,18 @@ def make_request(url, timeout=10):
139146
140147
141148def generate_load (
142- base_url , endpoints , duration = 60 , concurrent_requests = 5 , delay = 0.1
143- ):
149+ base_url : str ,
150+ endpoints : List [str ],
151+ duration : int = 60 ,
152+ concurrent_requests : int = 5 ,
153+ delay : float = 0.1 ,
154+ ) -> Dict [str , Any ]:
144155 """Generate HTTP load against specified endpoints."""
145156 end_time = time .time () + duration
146157 success_count = 0
147158 error_count = 0
148159
149- def worker ():
160+ def worker () -> None :
150161 nonlocal success_count , error_count
151162 while time .time () < end_time :
152163 for endpoint in endpoints :
@@ -179,10 +190,7 @@ def worker():
179190
180191
181192class TestHPAConfiguration :
182- """Test HPA resource configuration and basic functionality."""
183-
184- def test_hpa_resources_properly_configured (self ):
185- """Verify HPA resources have correct configuration."""
193+ def test_hpa_resources_properly_configured (self ) -> None :
186194 namespace = get_namespace ()
187195 result = kubectl_get ("hpa" , namespace = namespace )
188196
@@ -196,7 +204,6 @@ def test_hpa_resources_properly_configured(self):
196204 spec = hpa ["spec" ]
197205 hpa_name = hpa ["metadata" ]["name" ]
198206
199- # Check required fields
200207 assert "scaleTargetRef" in spec , (
201208 f"HPA { hpa_name } missing scaleTargetRef"
202209 )
@@ -206,19 +213,16 @@ def test_hpa_resources_properly_configured(self):
206213 f"HPA { hpa_name } missing metrics configuration"
207214 )
208215
209- # Validate replica bounds
210216 min_replicas = spec ["minReplicas" ]
211217 max_replicas = spec ["maxReplicas" ]
212218 assert min_replicas > 0 , f"HPA { hpa_name } minReplicas must be > 0"
213219 assert max_replicas > min_replicas , (
214220 f"HPA { hpa_name } maxReplicas must be > minReplicas"
215221 )
216222
217- # Check metrics configuration
218223 metrics = spec ["metrics" ]
219224 assert len (metrics ) > 0 , f"HPA { hpa_name } has no metrics configured"
220225
221- # Verify at least one metric is CPU
222226 cpu_metrics = [
223227 m
224228 for m in metrics
@@ -233,8 +237,7 @@ def test_hpa_resources_properly_configured(self):
233237 f"✅ HPA { hpa_name } : { min_replicas } -{ max_replicas } replicas, { len (metrics )} metrics"
234238 )
235239
236- def test_target_deployments_exist (self ):
237- """Verify HPA target deployments exist and are ready."""
240+ def test_target_deployments_exist (self ) -> None :
238241 namespace = get_namespace ()
239242 result = kubectl_get ("hpa" , namespace = namespace )
240243
@@ -281,9 +284,7 @@ def test_target_deployments_exist(self):
281284
282285
283286class TestCPUScaling :
284- """Test CPU-based autoscaling functionality."""
285-
286- def test_cpu_metrics_collection (self ):
287+ def test_cpu_metrics_collection (self ) -> None :
287288 """Verify CPU metrics are being collected for HPA targets."""
288289 namespace = get_namespace ()
289290 services = ["stac" , "raster" , "vector" ]
@@ -306,7 +307,7 @@ def test_cpu_metrics_collection(self):
306307 "No CPU metrics available for any service"
307308 )
308309
309- def test_hpa_cpu_utilization_calculation (self ):
310+ def test_hpa_cpu_utilization_calculation (self ) -> None :
310311 """Verify HPA calculates CPU utilization correctly."""
311312 namespace = get_namespace ()
312313 result = kubectl_get ("hpa" , namespace = namespace )
@@ -359,7 +360,7 @@ def test_hpa_cpu_utilization_calculation(self):
359360 else :
360361 print (f"⚠️ HPA { hpa_name } no CPU metrics available yet" )
361362
362- def test_cpu_resource_requests_alignment (self ):
363+ def test_cpu_resource_requests_alignment (self ) -> None :
363364 """Verify CPU resource requests are properly set for percentage calculations."""
364365 namespace = get_namespace ()
365366 services = ["stac" , "raster" , "vector" ]
@@ -419,7 +420,7 @@ class TestScalingBehavior:
419420 """Test actual scaling behavior under load."""
420421
421422 @pytest .mark .slow
422- def test_load_response_scaling (self ):
423+ def test_load_response_scaling (self ) -> None :
423424 """Generate load and verify scaling response (when possible)."""
424425 namespace = get_namespace ()
425426 base_url = get_base_url ()
@@ -433,7 +434,7 @@ def test_load_response_scaling(self):
433434 ]
434435
435436 # Check initial state
436- initial_pod_counts = {}
437+ initial_pod_counts : Dict [ str , int ] = {}
437438 services = ["stac" , "raster" , "vector" ]
438439
439440 for service in services :
@@ -473,7 +474,7 @@ def test_load_response_scaling(self):
473474 time .sleep (30 )
474475
475476 # Check final state
476- final_pod_counts = {}
477+ final_pod_counts : Dict [ str , int ] = {}
477478 for service in services :
478479 final_pod_counts [service ] = get_pod_count (namespace , service )
479480
@@ -501,7 +502,6 @@ def test_load_response_scaling(self):
501502 )
502503 print (f"Post-load HPA { hpa_name } CPU: { cpu_utilization } %" )
503504
504- # Verify load test was successful
505505 assert load_stats ["success_rate" ] > 0.8 , (
506506 f"Load test had low success rate: { load_stats ['success_rate' ]:.2%} "
507507 )
@@ -524,7 +524,7 @@ def test_load_response_scaling(self):
524524 "⚠️ No scaling occurred - may be due to CI resource constraints or low load thresholds"
525525 )
526526
527- def test_scaling_stabilization_windows (self ):
527+ def test_scaling_stabilization_windows (self ) -> None :
528528 """Verify HPA respects stabilization windows in configuration."""
529529 namespace = get_namespace ()
530530 result = kubectl_get ("hpa" , namespace = namespace )
@@ -538,7 +538,6 @@ def test_scaling_stabilization_windows(self):
538538 hpa_name = hpa ["metadata" ]["name" ]
539539 spec = hpa ["spec" ]
540540
541- # Check if behavior is configured
542541 behavior = spec .get ("behavior" , {})
543542 if not behavior :
544543 print (f"⚠️ HPA { hpa_name } has no scaling behavior configured" )
@@ -566,7 +565,7 @@ def test_scaling_stabilization_windows(self):
566565class TestRequestRateScaling :
567566 """Test request rate-based autoscaling (when available)."""
568567
569- def test_custom_metrics_for_request_rate (self ):
568+ def test_custom_metrics_for_request_rate (self ) -> None :
570569 """Check if custom metrics for request rate scaling are available."""
571570 namespace = get_namespace ()
572571
@@ -600,7 +599,7 @@ def test_custom_metrics_for_request_rate(self):
600599 "⚠️ No request rate metrics available - may require ingress controller metrics configuration"
601600 )
602601
603- def test_hpa_request_rate_metrics (self ):
602+ def test_hpa_request_rate_metrics (self ) -> None :
604603 """Verify HPA can access request rate metrics (when configured)."""
605604 namespace = get_namespace ()
606605 result = kubectl_get ("hpa" , namespace = namespace )
0 commit comments