1use chrono::{DateTime, Utc};
7use serde::{Deserialize, Serialize};
8use std::sync::Arc;
9
10use crate::lifecycle::{HealthStatus, ServiceStatus};
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct HealthReport {
15 pub status: HealthStatus,
16 pub services: Vec<ServiceHealth>,
17 pub timestamp: DateTime<Utc>,
18}
19
20impl Default for HealthReport {
21 fn default() -> Self {
22 Self {
23 status: HealthStatus::Healthy,
24 services: Vec::new(),
25 timestamp: Utc::now(),
26 }
27 }
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
32pub struct ServiceHealth {
33 pub name: String,
34 pub status: ServiceStatus,
35}
36
37pub type HealthChecker = Arc<dyn Fn() -> HealthReport + Send + Sync>;
38
39pub trait HealthSource: Send + Sync {
43 fn liveness(&self) -> HealthStatus;
44 fn readiness(&self) -> HealthStatus;
45
46 fn startup(&self) -> HealthStatus {
48 HealthStatus::Healthy
49 }
50}
51
52#[cfg(test)]
53mod tests {
54 use super::*;
55
56 #[test]
57 fn test_health_source_default_startup() {
58 struct MinimalSource;
59 impl HealthSource for MinimalSource {
60 fn liveness(&self) -> HealthStatus {
61 HealthStatus::Healthy
62 }
63
64 fn readiness(&self) -> HealthStatus {
65 HealthStatus::Healthy
66 }
67 }
68 let s = MinimalSource;
69 assert_eq!(s.startup(), HealthStatus::Healthy);
70 }
71
72 #[test]
73 fn test_health_source_custom_startup() {
74 struct BootingSource;
75 impl HealthSource for BootingSource {
76 fn liveness(&self) -> HealthStatus {
77 HealthStatus::Healthy
78 }
79
80 fn readiness(&self) -> HealthStatus {
81 HealthStatus::Healthy
82 }
83
84 fn startup(&self) -> HealthStatus {
85 HealthStatus::Unhealthy
86 }
87 }
88 let s = BootingSource;
89 assert_eq!(s.startup(), HealthStatus::Unhealthy);
90 }
91
92 #[test]
93 fn test_health_report_serialization() {
94 let report = HealthReport {
95 status: HealthStatus::Healthy,
96 services: vec![ServiceHealth {
97 name: "prometheus".to_string(),
98 status: ServiceStatus::Started,
99 }],
100 timestamp: chrono::Utc::now(),
101 };
102
103 let json = serde_json::to_string(&report).unwrap();
104 assert!(json.contains("Healthy"));
105 assert!(json.contains("prometheus"));
106 assert!(json.contains("Started"));
107 assert!(json.contains("timestamp"));
108 }
109
110 #[test]
111 fn test_health_report_default() {
112 let report = HealthReport::default();
113 assert_eq!(report.status, HealthStatus::Healthy);
114 assert!(report.services.is_empty());
115 assert!(report.timestamp <= chrono::Utc::now());
116 }
117}