@@ -259,4 +259,182 @@ mod tests {
259259 // 100 * 0.8 + 200 * 0.2 = 80 + 40 = 120
260260 assert ! ( ( health. avg_response_time_ms - 120.0 ) . abs( ) < 0.01 ) ;
261261 }
262+
263+ #[ test]
264+ fn test_health_status_default ( ) {
265+ let status = HealthStatus :: default ( ) ;
266+ assert_eq ! ( status, HealthStatus :: Unknown ) ;
267+ }
268+
269+ #[ test]
270+ fn test_challenge_health_new ( ) {
271+ let challenge_id = ChallengeId :: new ( ) ;
272+ let health = ChallengeHealth :: new ( challenge_id) ;
273+
274+ assert_eq ! ( health. challenge_id, challenge_id) ;
275+ assert_eq ! ( health. status, HealthStatus :: Unknown ) ;
276+ assert_eq ! ( health. last_check_at, 0 ) ;
277+ assert_eq ! ( health. consecutive_failures, 0 ) ;
278+ assert_eq ! ( health. avg_response_time_ms, 0.0 ) ;
279+ assert ! ( health. metrics. is_empty( ) ) ;
280+ }
281+
282+ #[ test]
283+ fn test_challenge_health_metrics ( ) {
284+ let mut health = ChallengeHealth :: new ( ChallengeId :: new ( ) ) ;
285+
286+ health. metrics . insert ( "cpu_usage" . to_string ( ) , 45.5 ) ;
287+ health. metrics . insert ( "memory_mb" . to_string ( ) , 512.0 ) ;
288+ health. metrics . insert ( "requests_per_sec" . to_string ( ) , 1000.0 ) ;
289+
290+ assert_eq ! ( health. metrics. len( ) , 3 ) ;
291+ assert_eq ! ( health. metrics. get( "cpu_usage" ) , Some ( & 45.5 ) ) ;
292+ assert_eq ! ( health. metrics. get( "memory_mb" ) , Some ( & 512.0 ) ) ;
293+ assert_eq ! ( health. metrics. get( "requests_per_sec" ) , Some ( & 1000.0 ) ) ;
294+ assert_eq ! ( health. metrics. get( "nonexistent" ) , None ) ;
295+ }
296+
297+ #[ test]
298+ fn test_health_config_default ( ) {
299+ let config = HealthConfig :: default ( ) ;
300+
301+ assert_eq ! ( config. check_interval, Duration :: from_secs( 30 ) ) ;
302+ assert_eq ! ( config. check_timeout, Duration :: from_secs( 5 ) ) ;
303+ assert_eq ! ( config. failure_threshold, 3 ) ;
304+ assert_eq ! ( config. recovery_threshold, 2 ) ;
305+ }
306+
307+ #[ test]
308+ fn test_health_config_custom ( ) {
309+ let config = HealthConfig {
310+ check_interval : Duration :: from_secs ( 60 ) ,
311+ check_timeout : Duration :: from_secs ( 10 ) ,
312+ failure_threshold : 5 ,
313+ recovery_threshold : 3 ,
314+ } ;
315+
316+ assert_eq ! ( config. check_interval, Duration :: from_secs( 60 ) ) ;
317+ assert_eq ! ( config. check_timeout, Duration :: from_secs( 10 ) ) ;
318+ assert_eq ! ( config. failure_threshold, 5 ) ;
319+ assert_eq ! ( config. recovery_threshold, 3 ) ;
320+ }
321+
322+ #[ test]
323+ fn test_health_monitor_with_config ( ) {
324+ let config = HealthConfig {
325+ check_interval : Duration :: from_secs ( 120 ) ,
326+ check_timeout : Duration :: from_secs ( 15 ) ,
327+ failure_threshold : 10 ,
328+ recovery_threshold : 5 ,
329+ } ;
330+
331+ let monitor = HealthMonitor :: with_config ( config) ;
332+ let monitor_config = monitor. config ( ) ;
333+
334+ assert_eq ! ( monitor_config. check_interval, Duration :: from_secs( 120 ) ) ;
335+ assert_eq ! ( monitor_config. check_timeout, Duration :: from_secs( 15 ) ) ;
336+ assert_eq ! ( monitor_config. failure_threshold, 10 ) ;
337+ assert_eq ! ( monitor_config. recovery_threshold, 5 ) ;
338+ }
339+
340+ #[ test]
341+ fn test_health_monitor_get_all_health ( ) {
342+ let monitor = HealthMonitor :: new ( ) ;
343+ let id1 = ChallengeId :: new ( ) ;
344+ let id2 = ChallengeId :: new ( ) ;
345+ let id3 = ChallengeId :: new ( ) ;
346+
347+ assert ! ( monitor. get_all_health( ) . is_empty( ) ) ;
348+
349+ monitor. register ( id1) ;
350+ monitor. register ( id2) ;
351+ monitor. register ( id3) ;
352+
353+ let all_health = monitor. get_all_health ( ) ;
354+ assert_eq ! ( all_health. len( ) , 3 ) ;
355+
356+ let ids: Vec < ChallengeId > = all_health. iter ( ) . map ( |h| h. challenge_id ) . collect ( ) ;
357+ assert ! ( ids. contains( & id1) ) ;
358+ assert ! ( ids. contains( & id2) ) ;
359+ assert ! ( ids. contains( & id3) ) ;
360+ }
361+
362+ #[ test]
363+ fn test_health_monitor_update_health ( ) {
364+ let monitor = HealthMonitor :: new ( ) ;
365+ let id = ChallengeId :: new ( ) ;
366+
367+ monitor. register ( id) ;
368+ let health = monitor. get_health ( & id) . expect ( "should be registered" ) ;
369+ assert_eq ! ( health. status, HealthStatus :: Unknown ) ;
370+
371+ monitor. update_health ( & id, HealthStatus :: Healthy ) ;
372+ let health = monitor. get_health ( & id) . expect ( "should be registered" ) ;
373+ assert_eq ! ( health. status, HealthStatus :: Healthy ) ;
374+ assert ! ( health. last_check_at > 0 ) ;
375+
376+ monitor. update_health ( & id, HealthStatus :: Degraded ( "high latency" . to_string ( ) ) ) ;
377+ let health = monitor. get_health ( & id) . expect ( "should be registered" ) ;
378+ assert_eq ! (
379+ health. status,
380+ HealthStatus :: Degraded ( "high latency" . to_string( ) )
381+ ) ;
382+
383+ monitor. update_health ( & id, HealthStatus :: Unhealthy ( "connection lost" . to_string ( ) ) ) ;
384+ let health = monitor. get_health ( & id) . expect ( "should be registered" ) ;
385+ assert_eq ! (
386+ health. status,
387+ HealthStatus :: Unhealthy ( "connection lost" . to_string( ) )
388+ ) ;
389+ }
390+
391+ #[ test]
392+ fn test_health_status_variants ( ) {
393+ let unknown = HealthStatus :: Unknown ;
394+ let healthy = HealthStatus :: Healthy ;
395+ let degraded = HealthStatus :: Degraded ( "slow response" . to_string ( ) ) ;
396+ let unhealthy = HealthStatus :: Unhealthy ( "service down" . to_string ( ) ) ;
397+
398+ assert_eq ! ( unknown, HealthStatus :: Unknown ) ;
399+ assert_eq ! ( healthy, HealthStatus :: Healthy ) ;
400+ assert_eq ! (
401+ degraded,
402+ HealthStatus :: Degraded ( "slow response" . to_string( ) )
403+ ) ;
404+ assert_eq ! (
405+ unhealthy,
406+ HealthStatus :: Unhealthy ( "service down" . to_string( ) )
407+ ) ;
408+
409+ assert_ne ! ( unknown, healthy) ;
410+ assert_ne ! ( healthy, degraded) ;
411+ assert_ne ! ( degraded, unhealthy) ;
412+
413+ let degraded_clone = degraded. clone ( ) ;
414+ assert_eq ! ( degraded, degraded_clone) ;
415+ }
416+
417+ #[ test]
418+ fn test_challenge_health_consecutive_successes ( ) {
419+ let mut health = ChallengeHealth :: new ( ChallengeId :: new ( ) ) ;
420+
421+ health. record_failure ( "error 1" . to_string ( ) ) ;
422+ health. record_failure ( "error 2" . to_string ( ) ) ;
423+ assert_eq ! ( health. consecutive_failures, 2 ) ;
424+ assert ! ( matches!( health. status, HealthStatus :: Degraded ( _) ) ) ;
425+
426+ health. record_success ( 50.0 ) ;
427+ assert_eq ! ( health. consecutive_failures, 0 ) ;
428+ assert_eq ! ( health. status, HealthStatus :: Healthy ) ;
429+
430+ health. record_failure ( "error 3" . to_string ( ) ) ;
431+ health. record_failure ( "error 4" . to_string ( ) ) ;
432+ health. record_failure ( "error 5" . to_string ( ) ) ;
433+ assert_eq ! ( health. consecutive_failures, 3 ) ;
434+ assert ! ( matches!( health. status, HealthStatus :: Unhealthy ( _) ) ) ;
435+
436+ health. record_success ( 75.0 ) ;
437+ assert_eq ! ( health. consecutive_failures, 0 ) ;
438+ assert_eq ! ( health. status, HealthStatus :: Healthy ) ;
439+ }
262440}
0 commit comments