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#[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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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#[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#[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#[derive(Debug)]
234pub struct ResolutionTracer {
235 enabled: bool,
236 traces: HashMap<String, Vec<ResolutionTrace>>,
237 stats: HashMap<String, ResolutionStats>,
238}
239
240impl ResolutionTracer {
241 pub fn new() -> Self {
243 Self {
244 enabled: false,
245 traces: HashMap::new(),
246 stats: HashMap::new(),
247 }
248 }
249
250 pub fn set_enabled(&mut self, enabled: bool) {
252 self.enabled = enabled;
253 }
254
255 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, })
266 }
267
268 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 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 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 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 pub fn get_stats(&self, service_name: &str) -> Option<&ResolutionStats> {
315 self.stats.get(service_name)
316 }
317
318 pub fn get_all_stats(&self) -> HashMap<String, ResolutionStats> {
320 self.stats.clone()
321 }
322
323 pub fn clear(&mut self) {
325 self.traces.clear();
326 self.stats.clear();
327 }
328
329 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 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 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#[derive(Debug)]
361pub struct ResolutionToken {
362 service_name: String,
363 start_time: Instant,
364 depth: usize,
365}
366
367#[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#[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#[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 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 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 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 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 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 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 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 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#[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
572pub struct ContainerHealthChecker {
574 container: Arc<IocContainer>,
575 checks: Vec<Box<dyn HealthCheck>>,
576}
577
578impl ContainerHealthChecker {
579 pub fn new(container: Arc<IocContainer>) -> Self {
581 let mut checker = Self {
582 container,
583 checks: Vec::new(),
584 };
585
586 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 pub fn add_check(&mut self, check: Box<dyn HealthCheck>) {
596 self.checks.push(check);
597 }
598
599 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 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
627pub trait HealthCheck: Send + Sync {
629 fn check(
631 &self,
632 container: &IocContainer,
633 ) -> std::pin::Pin<Box<dyn std::future::Future<Output = HealthCheckResult> + Send + '_>>;
634
635 fn name(&self) -> &str;
637
638 fn description(&self) -> &str;
640}
641
642#[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#[derive(Debug, Clone, PartialEq)]
654pub enum HealthStatus {
655 Healthy,
656 Warning,
657 Unhealthy,
658}
659
660#[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
715struct 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
757struct 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 let estimated_memory = 64; 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
803struct 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 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)); 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); }
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 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}