elif_core/container/
debug.rs

1use std::collections::HashMap;
2use std::fmt::Write;
3use std::sync::{Arc, Mutex};
4use std::time::{Duration, Instant};
5
6use crate::container::ioc_container::IocContainer;
7
8/// Container inspection utilities
9#[derive(Debug)]
10pub struct ContainerInspector {
11    container: Arc<IocContainer>,
12    resolution_tracer: Arc<Mutex<ResolutionTracer>>,
13    performance_profiler: Arc<Mutex<PerformanceProfiler>>,
14}
15
16impl ContainerInspector {
17    /// Create a new container inspector
18    pub fn new(container: Arc<IocContainer>) -> Self {
19        Self {
20            container,
21            resolution_tracer: Arc::new(Mutex::new(ResolutionTracer::new())),
22            performance_profiler: Arc::new(Mutex::new(PerformanceProfiler::new())),
23        }
24    }
25
26    /// Get basic container information
27    pub fn get_container_info(&self) -> ContainerInfo {
28        let stats = self.container.get_statistics();
29        let services = self.container.get_registered_services();
30
31        ContainerInfo {
32            is_built: self.container.is_built(),
33            service_count: stats.total_services,
34            singleton_count: stats.singleton_services,
35            scoped_count: stats.scoped_services,
36            transient_count: stats.transient_services,
37            cached_instances: stats.cached_instances,
38            registered_services: services,
39        }
40    }
41
42    /// Get detailed service information
43    pub fn inspect_service<T: 'static>(&self) -> Option<ServiceInfo> {
44        self.container
45            .get_service_info::<T>()
46            .map(|info| ServiceInfo {
47                type_name: std::any::type_name::<T>().to_string(),
48                registration_info: info,
49                is_registered: self.container.contains::<T>(),
50                resolution_count: self.get_resolution_count(std::any::type_name::<T>()),
51                last_resolved: self.get_last_resolution_time(std::any::type_name::<T>()),
52                average_resolution_time: self
53                    .get_average_resolution_time(std::any::type_name::<T>()),
54            })
55    }
56
57    /// Get resolution statistics for all services
58    pub fn get_resolution_stats(&self) -> HashMap<String, ResolutionStats> {
59        if let Ok(tracer) = self.resolution_tracer.lock() {
60            tracer.get_all_stats()
61        } else {
62            HashMap::new()
63        }
64    }
65
66    /// Get performance metrics
67    pub fn get_performance_metrics(&self) -> PerformanceMetrics {
68        if let Ok(profiler) = self.performance_profiler.lock() {
69            profiler.get_metrics()
70        } else {
71            PerformanceMetrics::default()
72        }
73    }
74
75    /// Enable/disable resolution tracing
76    pub fn set_tracing_enabled(&self, enabled: bool) {
77        if let Ok(mut tracer) = self.resolution_tracer.lock() {
78            tracer.set_enabled(enabled);
79        }
80    }
81
82    /// Enable/disable performance profiling
83    pub fn set_profiling_enabled(&self, enabled: bool) {
84        if let Ok(mut profiler) = self.performance_profiler.lock() {
85            profiler.set_enabled(enabled);
86        }
87    }
88
89    /// Clear all tracing and profiling data
90    pub fn clear_debug_data(&self) {
91        if let Ok(mut tracer) = self.resolution_tracer.lock() {
92            tracer.clear();
93        }
94        if let Ok(mut profiler) = self.performance_profiler.lock() {
95            profiler.clear();
96        }
97    }
98
99    /// Generate a comprehensive debug report
100    pub fn generate_debug_report(&self) -> String {
101        let mut report = String::new();
102
103        writeln!(report, "Container Debug Report").unwrap();
104        writeln!(report, "====================").unwrap();
105        writeln!(report, "Generated at: {:?}", std::time::SystemTime::now()).unwrap();
106        writeln!(report).unwrap();
107
108        // Container info
109        let info = self.get_container_info();
110        writeln!(report, "Container Information:").unwrap();
111        writeln!(report, "---------------------").unwrap();
112        writeln!(report, "Built: {}", info.is_built).unwrap();
113        writeln!(report, "Total Services: {}", info.service_count).unwrap();
114        writeln!(report, "  - Singletons: {}", info.singleton_count).unwrap();
115        writeln!(report, "  - Scoped: {}", info.scoped_count).unwrap();
116        writeln!(report, "  - Transient: {}", info.transient_count).unwrap();
117        writeln!(report, "Cached Instances: {}", info.cached_instances).unwrap();
118        writeln!(report).unwrap();
119
120        // Resolution statistics
121        let stats = self.get_resolution_stats();
122        if !stats.is_empty() {
123            writeln!(report, "Resolution Statistics:").unwrap();
124            writeln!(report, "---------------------").unwrap();
125
126            let mut sorted_stats: Vec<_> = stats.iter().collect();
127            sorted_stats.sort_by_key(|(_, stat)| std::cmp::Reverse(stat.total_resolutions));
128
129            for (service, stat) in sorted_stats.iter().take(10) {
130                writeln!(
131                    report,
132                    "{}: {} resolutions, avg {:.2}ms",
133                    service, stat.total_resolutions, stat.average_duration_ms
134                )
135                .unwrap();
136            }
137            writeln!(report).unwrap();
138        }
139
140        // Performance metrics
141        let metrics = self.get_performance_metrics();
142        writeln!(report, "Performance Metrics:").unwrap();
143        writeln!(report, "-------------------").unwrap();
144        writeln!(
145            report,
146            "Total Resolution Time: {:.2}ms",
147            metrics.total_resolution_time_ms
148        )
149        .unwrap();
150        writeln!(
151            report,
152            "Average Resolution Time: {:.2}ms",
153            metrics.average_resolution_time_ms
154        )
155        .unwrap();
156        writeln!(
157            report,
158            "Slowest Resolution: {:.2}ms ({})",
159            metrics.slowest_resolution_ms,
160            metrics.slowest_service.as_deref().unwrap_or("Unknown")
161        )
162        .unwrap();
163        writeln!(
164            report,
165            "Memory Usage (estimated): {} bytes",
166            metrics.estimated_memory_usage
167        )
168        .unwrap();
169        writeln!(report).unwrap();
170
171        // Registered services
172        writeln!(report, "Registered Services:").unwrap();
173        writeln!(report, "-------------------").unwrap();
174        for (i, service) in info.registered_services.iter().enumerate() {
175            writeln!(report, "{}. {}", i + 1, service).unwrap();
176        }
177
178        report
179    }
180
181    /// Get resolution count for a service
182    fn get_resolution_count(&self, service_name: &str) -> usize {
183        if let Ok(tracer) = self.resolution_tracer.lock() {
184            tracer.get_resolution_count(service_name)
185        } else {
186            0
187        }
188    }
189
190    /// Get last resolution time for a service
191    fn get_last_resolution_time(&self, service_name: &str) -> Option<Instant> {
192        if let Ok(tracer) = self.resolution_tracer.lock() {
193            tracer.get_last_resolution_time(service_name)
194        } else {
195            None
196        }
197    }
198
199    /// Get average resolution time for a service
200    fn get_average_resolution_time(&self, service_name: &str) -> Option<Duration> {
201        if let Ok(tracer) = self.resolution_tracer.lock() {
202            tracer.get_average_resolution_time(service_name)
203        } else {
204            None
205        }
206    }
207}
208
209/// Container information for inspection
210#[derive(Debug, Clone)]
211pub struct ContainerInfo {
212    pub is_built: bool,
213    pub service_count: usize,
214    pub singleton_count: usize,
215    pub scoped_count: usize,
216    pub transient_count: usize,
217    pub cached_instances: usize,
218    pub registered_services: Vec<String>,
219}
220
221/// Service information for inspection
222#[derive(Debug, Clone)]
223pub struct ServiceInfo {
224    pub type_name: String,
225    pub registration_info: String,
226    pub is_registered: bool,
227    pub resolution_count: usize,
228    pub last_resolved: Option<Instant>,
229    pub average_resolution_time: Option<Duration>,
230}
231
232/// Resolution tracing for debugging dependency resolution
233#[derive(Debug)]
234pub struct ResolutionTracer {
235    enabled: bool,
236    traces: HashMap<String, Vec<ResolutionTrace>>,
237    stats: HashMap<String, ResolutionStats>,
238}
239
240impl ResolutionTracer {
241    /// Create a new resolution tracer
242    pub fn new() -> Self {
243        Self {
244            enabled: false,
245            traces: HashMap::new(),
246            stats: HashMap::new(),
247        }
248    }
249
250    /// Enable or disable tracing
251    pub fn set_enabled(&mut self, enabled: bool) {
252        self.enabled = enabled;
253    }
254
255    /// Record the start of a service resolution
256    pub fn start_resolution(&mut self, service_name: &str) -> Option<ResolutionToken> {
257        if !self.enabled {
258            return None;
259        }
260
261        Some(ResolutionToken {
262            service_name: service_name.to_string(),
263            start_time: Instant::now(),
264            depth: 0, // This would be calculated based on call stack
265        })
266    }
267
268    /// Record the completion of a service resolution
269    pub fn complete_resolution(
270        &mut self,
271        token: ResolutionToken,
272        success: bool,
273        error: Option<String>,
274    ) {
275        if !self.enabled {
276            return;
277        }
278
279        let duration = token.start_time.elapsed();
280
281        // Record trace
282        let trace = ResolutionTrace {
283            service_name: token.service_name.clone(),
284            start_time: token.start_time,
285            duration,
286            success,
287            error,
288            depth: token.depth,
289        };
290
291        self.traces
292            .entry(token.service_name.clone())
293            .or_default()
294            .push(trace);
295
296        // Update statistics
297        let stats = self
298            .stats
299            .entry(token.service_name.clone())
300            .or_insert_with(|| ResolutionStats::new(token.service_name.clone()));
301
302        stats.record_resolution(duration, success);
303    }
304
305    /// Get resolution traces for a service
306    pub fn get_traces(&self, service_name: &str) -> Vec<&ResolutionTrace> {
307        self.traces
308            .get(service_name)
309            .map(|traces| traces.iter().collect())
310            .unwrap_or_default()
311    }
312
313    /// Get resolution statistics for a service
314    pub fn get_stats(&self, service_name: &str) -> Option<&ResolutionStats> {
315        self.stats.get(service_name)
316    }
317
318    /// Get all resolution statistics
319    pub fn get_all_stats(&self) -> HashMap<String, ResolutionStats> {
320        self.stats.clone()
321    }
322
323    /// Clear all tracing data
324    pub fn clear(&mut self) {
325        self.traces.clear();
326        self.stats.clear();
327    }
328
329    /// Get resolution count for a service
330    pub fn get_resolution_count(&self, service_name: &str) -> usize {
331        self.stats
332            .get(service_name)
333            .map(|s| s.total_resolutions)
334            .unwrap_or(0)
335    }
336
337    /// Get last resolution time
338    pub fn get_last_resolution_time(&self, service_name: &str) -> Option<Instant> {
339        self.traces
340            .get(service_name)?
341            .last()
342            .map(|trace| trace.start_time)
343    }
344
345    /// Get average resolution time
346    pub fn get_average_resolution_time(&self, service_name: &str) -> Option<Duration> {
347        self.stats
348            .get(service_name)
349            .map(|s| Duration::from_nanos((s.average_duration_ms * 1_000_000.0) as u64))
350    }
351}
352
353impl Default for ResolutionTracer {
354    fn default() -> Self {
355        Self::new()
356    }
357}
358
359/// Token representing an ongoing service resolution
360#[derive(Debug)]
361pub struct ResolutionToken {
362    service_name: String,
363    start_time: Instant,
364    depth: usize,
365}
366
367/// Trace record for a single service resolution
368#[derive(Debug, Clone)]
369pub struct ResolutionTrace {
370    pub service_name: String,
371    pub start_time: Instant,
372    pub duration: Duration,
373    pub success: bool,
374    pub error: Option<String>,
375    pub depth: usize,
376}
377
378/// Resolution statistics for a service
379#[derive(Debug, Clone)]
380pub struct ResolutionStats {
381    pub service_name: String,
382    pub total_resolutions: usize,
383    pub successful_resolutions: usize,
384    pub failed_resolutions: usize,
385    pub total_duration_ms: f64,
386    pub average_duration_ms: f64,
387    pub min_duration_ms: f64,
388    pub max_duration_ms: f64,
389    pub last_resolution: Option<Instant>,
390}
391
392impl ResolutionStats {
393    pub fn new(service_name: String) -> Self {
394        Self {
395            service_name,
396            total_resolutions: 0,
397            successful_resolutions: 0,
398            failed_resolutions: 0,
399            total_duration_ms: 0.0,
400            average_duration_ms: 0.0,
401            min_duration_ms: f64::MAX,
402            max_duration_ms: 0.0,
403            last_resolution: None,
404        }
405    }
406
407    pub fn record_resolution(&mut self, duration: Duration, success: bool) {
408        let duration_ms = duration.as_secs_f64() * 1000.0;
409
410        self.total_resolutions += 1;
411        if success {
412            self.successful_resolutions += 1;
413        } else {
414            self.failed_resolutions += 1;
415        }
416
417        self.total_duration_ms += duration_ms;
418        self.average_duration_ms = self.total_duration_ms / self.total_resolutions as f64;
419
420        self.min_duration_ms = self.min_duration_ms.min(duration_ms);
421        self.max_duration_ms = self.max_duration_ms.max(duration_ms);
422
423        self.last_resolution = Some(Instant::now());
424    }
425}
426
427/// Performance profiler for container operations
428#[derive(Debug)]
429pub struct PerformanceProfiler {
430    enabled: bool,
431    resolution_times: Vec<(String, Duration)>,
432    memory_snapshots: Vec<(Instant, usize)>,
433    start_time: Option<Instant>,
434}
435
436impl PerformanceProfiler {
437    /// Create a new performance profiler
438    pub fn new() -> Self {
439        Self {
440            enabled: false,
441            resolution_times: Vec::new(),
442            memory_snapshots: Vec::new(),
443            start_time: None,
444        }
445    }
446
447    /// Enable or disable profiling
448    pub fn set_enabled(&mut self, enabled: bool) {
449        self.enabled = enabled;
450        if enabled && self.start_time.is_none() {
451            self.start_time = Some(Instant::now());
452        }
453    }
454
455    /// Record a service resolution time
456    pub fn record_resolution_time(&mut self, service_name: &str, duration: Duration) {
457        if self.enabled {
458            self.resolution_times
459                .push((service_name.to_string(), duration));
460        }
461    }
462
463    /// Record a memory snapshot
464    pub fn record_memory_snapshot(&mut self, memory_usage: usize) {
465        if self.enabled {
466            self.memory_snapshots.push((Instant::now(), memory_usage));
467        }
468    }
469
470    /// Get performance metrics
471    pub fn get_metrics(&self) -> PerformanceMetrics {
472        if self.resolution_times.is_empty() {
473            return PerformanceMetrics::default();
474        }
475
476        let total_time: Duration = self
477            .resolution_times
478            .iter()
479            .map(|(_, duration)| *duration)
480            .sum();
481
482        let avg_time = total_time / self.resolution_times.len() as u32;
483
484        let (slowest_service, slowest_time) = self
485            .resolution_times
486            .iter()
487            .max_by_key(|(_, duration)| *duration)
488            .map(|(name, duration)| (name.clone(), *duration))
489            .unwrap_or_else(|| ("Unknown".to_string(), Duration::default()));
490
491        let estimated_memory = self
492            .memory_snapshots
493            .last()
494            .map(|(_, memory)| *memory)
495            .unwrap_or(0);
496
497        PerformanceMetrics {
498            total_resolution_time_ms: total_time.as_secs_f64() * 1000.0,
499            average_resolution_time_ms: avg_time.as_secs_f64() * 1000.0,
500            slowest_resolution_ms: slowest_time.as_secs_f64() * 1000.0,
501            slowest_service: Some(slowest_service),
502            total_resolutions: self.resolution_times.len(),
503            estimated_memory_usage: estimated_memory,
504            profiling_duration: self.start_time.map(|start| start.elapsed()),
505        }
506    }
507
508    /// Get slowest resolutions
509    pub fn get_slowest_resolutions(&self, count: usize) -> Vec<(String, Duration)> {
510        let mut sorted = self.resolution_times.clone();
511        sorted.sort_by_key(|(_, duration)| std::cmp::Reverse(*duration));
512        sorted.into_iter().take(count).collect()
513    }
514
515    /// Get most frequent resolutions
516    pub fn get_most_frequent(&self, count: usize) -> Vec<(String, usize)> {
517        let mut frequency: HashMap<String, usize> = HashMap::new();
518
519        for (service_name, _) in &self.resolution_times {
520            *frequency.entry(service_name.clone()).or_insert(0) += 1;
521        }
522
523        let mut sorted: Vec<_> = frequency.into_iter().collect();
524        sorted.sort_by_key(|(_, freq)| std::cmp::Reverse(*freq));
525        sorted.into_iter().take(count).collect()
526    }
527
528    /// Clear all profiling data
529    pub fn clear(&mut self) {
530        self.resolution_times.clear();
531        self.memory_snapshots.clear();
532        self.start_time = if self.enabled {
533            Some(Instant::now())
534        } else {
535            None
536        };
537    }
538}
539
540impl Default for PerformanceProfiler {
541    fn default() -> Self {
542        Self::new()
543    }
544}
545
546/// Performance metrics summary
547#[derive(Debug, Clone)]
548pub struct PerformanceMetrics {
549    pub total_resolution_time_ms: f64,
550    pub average_resolution_time_ms: f64,
551    pub slowest_resolution_ms: f64,
552    pub slowest_service: Option<String>,
553    pub total_resolutions: usize,
554    pub estimated_memory_usage: usize,
555    pub profiling_duration: Option<Duration>,
556}
557
558impl Default for PerformanceMetrics {
559    fn default() -> Self {
560        Self {
561            total_resolution_time_ms: 0.0,
562            average_resolution_time_ms: 0.0,
563            slowest_resolution_ms: 0.0,
564            slowest_service: None,
565            total_resolutions: 0,
566            estimated_memory_usage: 0,
567            profiling_duration: None,
568        }
569    }
570}
571
572/// Health check system for container
573pub struct ContainerHealthChecker {
574    container: Arc<IocContainer>,
575    checks: Vec<Box<dyn HealthCheck>>,
576}
577
578impl ContainerHealthChecker {
579    /// Create a new health checker
580    pub fn new(container: Arc<IocContainer>) -> Self {
581        let mut checker = Self {
582            container,
583            checks: Vec::new(),
584        };
585
586        // Add default health checks
587        checker.add_check(Box::new(CircularDependencyCheck));
588        checker.add_check(Box::new(MemoryUsageCheck { max_memory_mb: 512 }));
589        checker.add_check(Box::new(SingletonHealthCheck));
590
591        checker
592    }
593
594    /// Add a custom health check
595    pub fn add_check(&mut self, check: Box<dyn HealthCheck>) {
596        self.checks.push(check);
597    }
598
599    /// Run all health checks
600    pub async fn check_health(&self) -> HealthReport {
601        let mut results = Vec::new();
602        let mut overall_status = HealthStatus::Healthy;
603
604        for check in &self.checks {
605            let result = check.check(&self.container).await;
606
607            // Update overall status
608            match result.status {
609                HealthStatus::Unhealthy => overall_status = HealthStatus::Unhealthy,
610                HealthStatus::Warning if overall_status == HealthStatus::Healthy => {
611                    overall_status = HealthStatus::Warning;
612                }
613                _ => {}
614            }
615
616            results.push(result);
617        }
618
619        HealthReport {
620            overall_status,
621            checks: results,
622            timestamp: std::time::SystemTime::now(),
623        }
624    }
625}
626
627/// Health check trait
628pub trait HealthCheck: Send + Sync {
629    /// Run the health check
630    fn check(
631        &self,
632        container: &IocContainer,
633    ) -> std::pin::Pin<Box<dyn std::future::Future<Output = HealthCheckResult> + Send + '_>>;
634
635    /// Get the name of this health check
636    fn name(&self) -> &str;
637
638    /// Get the description of what this check does
639    fn description(&self) -> &str;
640}
641
642/// Health check result
643#[derive(Debug, Clone)]
644pub struct HealthCheckResult {
645    pub name: String,
646    pub status: HealthStatus,
647    pub message: String,
648    pub details: Option<String>,
649    pub duration: Duration,
650}
651
652/// Health status enumeration
653#[derive(Debug, Clone, PartialEq)]
654pub enum HealthStatus {
655    Healthy,
656    Warning,
657    Unhealthy,
658}
659
660/// Overall health report
661#[derive(Debug)]
662pub struct HealthReport {
663    pub overall_status: HealthStatus,
664    pub checks: Vec<HealthCheckResult>,
665    pub timestamp: std::time::SystemTime,
666}
667
668impl std::fmt::Display for HealthReport {
669    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
670        writeln!(f, "Container Health Report")?;
671        writeln!(f, "======================")?;
672        writeln!(f, "Timestamp: {:?}", self.timestamp)?;
673        writeln!(f, "Overall Status: {:?}", self.overall_status)?;
674        writeln!(f)?;
675
676        let status_symbol = match self.overall_status {
677            HealthStatus::Healthy => "✅",
678            HealthStatus::Warning => "⚠️ ",
679            HealthStatus::Unhealthy => "❌",
680        };
681
682        writeln!(
683            f,
684            "{} Container is {:?}",
685            status_symbol, self.overall_status
686        )?;
687        writeln!(f)?;
688
689        writeln!(f, "Individual Checks:")?;
690        writeln!(f, "------------------")?;
691
692        for check in &self.checks {
693            let symbol = match check.status {
694                HealthStatus::Healthy => "✅",
695                HealthStatus::Warning => "⚠️ ",
696                HealthStatus::Unhealthy => "❌",
697            };
698
699            writeln!(f, "{} {}: {}", symbol, check.name, check.message)?;
700            if let Some(details) = &check.details {
701                writeln!(f, "   Details: {}", details)?;
702            }
703            writeln!(
704                f,
705                "   Duration: {:.2}ms",
706                check.duration.as_secs_f64() * 1000.0
707            )?;
708            writeln!(f)?;
709        }
710
711        Ok(())
712    }
713}
714
715/// Circular dependency health check
716struct CircularDependencyCheck;
717
718impl HealthCheck for CircularDependencyCheck {
719    fn check(
720        &self,
721        container: &IocContainer,
722    ) -> std::pin::Pin<Box<dyn std::future::Future<Output = HealthCheckResult> + Send + '_>> {
723        let start = Instant::now();
724        let name = self.name().to_string();
725
726        match container.validate() {
727            Ok(()) => Box::pin(async move {
728                HealthCheckResult {
729                    name,
730                    status: HealthStatus::Healthy,
731                    message: "No circular dependencies detected".to_string(),
732                    details: None,
733                    duration: start.elapsed(),
734                }
735            }),
736            Err(e) => Box::pin(async move {
737                HealthCheckResult {
738                    name,
739                    status: HealthStatus::Unhealthy,
740                    message: "Circular dependency detected".to_string(),
741                    details: Some(e.to_string()),
742                    duration: start.elapsed(),
743                }
744            }),
745        }
746    }
747
748    fn name(&self) -> &str {
749        "Circular Dependency Check"
750    }
751
752    fn description(&self) -> &str {
753        "Checks for circular dependencies in the service graph"
754    }
755}
756
757/// Memory usage health check
758struct MemoryUsageCheck {
759    max_memory_mb: usize,
760}
761
762impl HealthCheck for MemoryUsageCheck {
763    fn check(
764        &self,
765        _container: &IocContainer,
766    ) -> std::pin::Pin<Box<dyn std::future::Future<Output = HealthCheckResult> + Send + '_>> {
767        let start = Instant::now();
768        let name = self.name().to_string();
769        let max_memory_mb = self.max_memory_mb;
770
771        Box::pin(async move {
772            // This is a simplified memory check - in a real implementation,
773            // you'd get actual memory usage from the system
774            let estimated_memory = 64; // MB
775
776            let status = if estimated_memory > max_memory_mb {
777                HealthStatus::Unhealthy
778            } else if estimated_memory > max_memory_mb / 2 {
779                HealthStatus::Warning
780            } else {
781                HealthStatus::Healthy
782            };
783
784            HealthCheckResult {
785                name,
786                status,
787                message: format!("Memory usage: {} MB", estimated_memory),
788                details: Some(format!("Limit: {} MB", max_memory_mb)),
789                duration: start.elapsed(),
790            }
791        })
792    }
793
794    fn name(&self) -> &str {
795        "Memory Usage Check"
796    }
797
798    fn description(&self) -> &str {
799        "Monitors container memory usage"
800    }
801}
802
803/// Singleton health check
804struct SingletonHealthCheck;
805
806impl HealthCheck for SingletonHealthCheck {
807    fn check(
808        &self,
809        container: &IocContainer,
810    ) -> std::pin::Pin<Box<dyn std::future::Future<Output = HealthCheckResult> + Send + '_>> {
811        let start = Instant::now();
812        let stats = container.get_statistics();
813        let name = self.name().to_string();
814
815        Box::pin(async move {
816            // Check if there are too many singletons (potential memory issues)
817            let singleton_ratio = if stats.total_services > 0 {
818                stats.singleton_services as f64 / stats.total_services as f64
819            } else {
820                0.0
821            };
822
823            let status = if singleton_ratio > 0.8 {
824                HealthStatus::Warning
825            } else {
826                HealthStatus::Healthy
827            };
828
829            HealthCheckResult {
830                name,
831                status,
832                message: format!("Singleton ratio: {:.1}%", singleton_ratio * 100.0),
833                details: Some(format!(
834                    "{} singletons out of {} total services",
835                    stats.singleton_services, stats.total_services
836                )),
837                duration: start.elapsed(),
838            }
839        })
840    }
841
842    fn name(&self) -> &str {
843        "Singleton Health Check"
844    }
845
846    fn description(&self) -> &str {
847        "Monitors singleton service ratio"
848    }
849}
850
851#[cfg(test)]
852mod tests {
853    use super::*;
854    use crate::container::ioc_container::IocContainer;
855    use std::time::Duration;
856
857    #[test]
858    fn test_resolution_tracer() {
859        let mut tracer = ResolutionTracer::new();
860        tracer.set_enabled(true);
861
862        let token = tracer.start_resolution("TestService").unwrap();
863        std::thread::sleep(Duration::from_millis(1)); // Small delay for testing
864        tracer.complete_resolution(token, true, None);
865
866        let stats = tracer.get_stats("TestService").unwrap();
867        assert_eq!(stats.total_resolutions, 1);
868        assert_eq!(stats.successful_resolutions, 1);
869        assert!(stats.average_duration_ms > 0.0);
870    }
871
872    #[test]
873    fn test_performance_profiler() {
874        let mut profiler = PerformanceProfiler::new();
875        profiler.set_enabled(true);
876
877        profiler.record_resolution_time("Service1", Duration::from_millis(10));
878        profiler.record_resolution_time("Service2", Duration::from_millis(5));
879        profiler.record_resolution_time("Service1", Duration::from_millis(15));
880
881        let metrics = profiler.get_metrics();
882        assert_eq!(metrics.total_resolutions, 3);
883        assert_eq!(metrics.total_resolution_time_ms, 30.0);
884        assert_eq!(metrics.average_resolution_time_ms, 10.0);
885
886        let slowest = profiler.get_slowest_resolutions(2);
887        assert_eq!(slowest.len(), 2);
888        assert_eq!(slowest[0].1, Duration::from_millis(15));
889
890        let frequent = profiler.get_most_frequent(2);
891        assert_eq!(frequent.len(), 2);
892        assert_eq!(frequent[0].1, 2); // Service1 appears twice
893    }
894
895    #[test]
896    fn test_container_inspector() {
897        let container = IocContainer::new();
898        let inspector = ContainerInspector::new(Arc::new(container));
899
900        let info = inspector.get_container_info();
901        assert_eq!(info.service_count, 0);
902        assert!(!info.is_built);
903
904        let report = inspector.generate_debug_report();
905        assert!(report.contains("Container Debug Report"));
906        assert!(report.contains("Container Information"));
907    }
908
909    #[tokio::test]
910    async fn test_health_checker() {
911        let container = IocContainer::new();
912        let health_checker = ContainerHealthChecker::new(Arc::new(container));
913
914        let report = health_checker.check_health().await;
915
916        // Should have at least the default health checks
917        assert!(!report.checks.is_empty());
918
919        let report_str = report.to_string();
920        assert!(report_str.contains("Container Health Report"));
921        assert!(report_str.contains("Overall Status"));
922    }
923
924    #[test]
925    fn test_resolution_stats() {
926        let mut stats = ResolutionStats::new("TestService".to_string());
927
928        stats.record_resolution(Duration::from_millis(10), true);
929        stats.record_resolution(Duration::from_millis(20), true);
930        stats.record_resolution(Duration::from_millis(5), false);
931
932        assert_eq!(stats.total_resolutions, 3);
933        assert_eq!(stats.successful_resolutions, 2);
934        assert_eq!(stats.failed_resolutions, 1);
935        assert_eq!(stats.average_duration_ms, (10.0 + 20.0 + 5.0) / 3.0);
936        assert_eq!(stats.min_duration_ms, 5.0);
937        assert_eq!(stats.max_duration_ms, 20.0);
938    }
939}