agentic_payments/system/
health.rs

1//! Health monitoring for the verification system
2
3use crate::error::{Error, Result};
4use crate::system::pool::AgentPool;
5use serde::{Deserialize, Serialize};
6use std::time::{Duration, Instant};
7
8/// System health status
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
10pub enum HealthStatus {
11    /// System is healthy
12    Healthy,
13    /// System is degraded but operational
14    Degraded,
15    /// System is unhealthy
16    Unhealthy,
17}
18
19/// Health check result
20#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct HealthCheck {
22    /// Health status
23    pub status: HealthStatus,
24    /// Last check timestamp
25    pub last_check: i64,
26    /// Check duration in milliseconds
27    pub duration_ms: u64,
28    /// Error message if unhealthy
29    pub message: Option<String>,
30}
31
32impl HealthCheck {
33    /// Create a healthy check result
34    pub fn healthy(duration_ms: u64) -> Self {
35        Self {
36            status: HealthStatus::Healthy,
37            last_check: chrono::Utc::now().timestamp(),
38            duration_ms,
39            message: None,
40        }
41    }
42
43    /// Create a degraded check result
44    pub fn degraded(duration_ms: u64, message: String) -> Self {
45        Self {
46            status: HealthStatus::Degraded,
47            last_check: chrono::Utc::now().timestamp(),
48            duration_ms,
49            message: Some(message),
50        }
51    }
52
53    /// Create an unhealthy check result
54    pub fn unhealthy(duration_ms: u64, message: String) -> Self {
55        Self {
56            status: HealthStatus::Unhealthy,
57            last_check: chrono::Utc::now().timestamp(),
58            duration_ms,
59            message: Some(message),
60        }
61    }
62}
63
64/// System health monitoring
65pub struct SystemHealth {
66    status: HealthStatus,
67    last_check: Option<Instant>,
68    check_interval: Duration,
69}
70
71impl SystemHealth {
72    /// Create a new system health monitor
73    pub fn new() -> Self {
74        Self {
75            status: HealthStatus::Healthy,
76            last_check: None,
77            check_interval: Duration::from_secs(30),
78        }
79    }
80
81    /// Get current health status
82    pub fn status(&self) -> HealthStatus {
83        self.status
84    }
85
86    /// Set health status
87    pub fn set_status(&mut self, status: HealthStatus) {
88        self.status = status;
89    }
90
91    /// Check if health check is due
92    pub fn is_check_due(&self) -> bool {
93        match self.last_check {
94            None => true,
95            Some(last) => last.elapsed() >= self.check_interval,
96        }
97    }
98
99    /// Perform system health check
100    pub async fn check_system(&mut self) -> Result<HealthCheck> {
101        let start = Instant::now();
102
103        // Update last check time
104        self.last_check = Some(start);
105
106        // System is healthy by default
107        let duration_ms = start.elapsed().as_millis() as u64;
108        Ok(HealthCheck::healthy(duration_ms))
109    }
110
111    /// Check agent pool health
112    pub async fn check_agent_pool(&mut self, pool: &AgentPool) -> Result<HealthCheck> {
113        let start = Instant::now();
114
115        if pool.is_empty() {
116            let duration_ms = start.elapsed().as_millis() as u64;
117            self.status = HealthStatus::Unhealthy;
118            return Ok(HealthCheck::unhealthy(
119                duration_ms,
120                "Agent pool is empty".to_string(),
121            ));
122        }
123
124        // Check if minimum number of agents are available
125        if pool.size() < 3 {
126            let duration_ms = start.elapsed().as_millis() as u64;
127            self.status = HealthStatus::Degraded;
128            return Ok(HealthCheck::degraded(
129                duration_ms,
130                format!("Only {} agents available (minimum: 3)", pool.size()),
131            ));
132        }
133
134        // Perform health check on all agents
135        if let Err(e) = pool.health_check_all().await {
136            let duration_ms = start.elapsed().as_millis() as u64;
137            self.status = HealthStatus::Degraded;
138            return Ok(HealthCheck::degraded(
139                duration_ms,
140                format!("Some agents failed health check: {}", e),
141            ));
142        }
143
144        let duration_ms = start.elapsed().as_millis() as u64;
145        self.status = HealthStatus::Healthy;
146        Ok(HealthCheck::healthy(duration_ms))
147    }
148}
149
150impl Default for SystemHealth {
151    fn default() -> Self {
152        Self::new()
153    }
154}
155
156#[cfg(test)]
157mod tests {
158    use super::*;
159
160    #[test]
161    fn test_health_status() {
162        let mut health = SystemHealth::new();
163        assert_eq!(health.status(), HealthStatus::Healthy);
164
165        health.set_status(HealthStatus::Degraded);
166        assert_eq!(health.status(), HealthStatus::Degraded);
167    }
168
169    #[test]
170    fn test_health_check_creation() {
171        let check = HealthCheck::healthy(100);
172        assert_eq!(check.status, HealthStatus::Healthy);
173        assert!(check.message.is_none());
174
175        let check = HealthCheck::degraded(100, "Warning".to_string());
176        assert_eq!(check.status, HealthStatus::Degraded);
177        assert!(check.message.is_some());
178    }
179
180    #[tokio::test]
181    async fn test_system_health_check() {
182        let mut health = SystemHealth::new();
183        let result = health.check_system().await.unwrap();
184        assert_eq!(result.status, HealthStatus::Healthy);
185    }
186}