#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod tests {
use super::*;
use anyhow::Error;
struct TestService {
fail_count: std::sync::atomic::AtomicU32,
}
#[async_trait]
impl Service for TestService {
type Input = String;
type Output = String;
type Error = Error;
async fn process(&self, input: Self::Input) -> Result<Self::Output, Self::Error> {
let count = self.fail_count.fetch_add(1, Ordering::Relaxed);
if count < 2 {
Err(anyhow::anyhow!("Simulated failure"))
} else {
Ok(format!("Processed: {}", input))
}
}
}
#[tokio::test]
async fn test_lifecycle_wrapper() {
let service = TestService {
fail_count: std::sync::atomic::AtomicU32::new(0),
};
let wrapper = LifecycleWrapper::new(service, Duration::from_millis(100));
wrapper.start().await.unwrap();
assert_eq!(wrapper.get_state().await, ServiceState::Running);
let _ = wrapper.process("test1".to_string()).await;
let _ = wrapper.process("test2".to_string()).await;
let result = wrapper.process("test3".to_string()).await.unwrap();
assert_eq!(result, "Processed: test3");
wrapper.stop().await.unwrap();
assert_eq!(wrapper.get_state().await, ServiceState::Stopped);
}
#[tokio::test]
async fn test_health_status() {
let service = TestService {
fail_count: std::sync::atomic::AtomicU32::new(0),
};
let wrapper = LifecycleWrapper::new(service, Duration::from_millis(100));
wrapper.start().await.unwrap();
let health = wrapper.get_health().await;
assert_eq!(health.state, ServiceState::Running);
assert!(health.uptime_seconds < 3600);
wrapper.stop().await.unwrap();
}
#[test]
fn test_service_state_variants() {
let states = [
ServiceState::Uninitialized,
ServiceState::Starting,
ServiceState::Running,
ServiceState::Degraded,
ServiceState::Stopping,
ServiceState::Stopped,
ServiceState::Failed,
];
assert_eq!(states.len(), 7);
}
#[test]
fn test_service_state_clone() {
let state = ServiceState::Running;
let cloned = state.clone();
assert_eq!(cloned, ServiceState::Running);
}
#[test]
fn test_service_state_copy() {
let state = ServiceState::Degraded;
let copied = state;
assert_eq!(copied, ServiceState::Degraded);
}
#[test]
fn test_service_state_debug() {
let state = ServiceState::Running;
let debug = format!("{:?}", state);
assert!(debug.contains("Running"));
}
#[test]
fn test_service_state_equality() {
assert_eq!(ServiceState::Running, ServiceState::Running);
assert_ne!(ServiceState::Running, ServiceState::Stopped);
}
#[test]
fn test_service_state_serialization() {
let state = ServiceState::Failed;
let json = serde_json::to_string(&state).unwrap();
let deserialized: ServiceState = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized, ServiceState::Failed);
}
#[test]
fn test_health_status_creation() {
let status = HealthStatus {
state: ServiceState::Running,
message: "OK".to_string(),
last_check: std::time::SystemTime::now(),
uptime_seconds: 100,
metrics: ServiceMetrics::default(),
};
assert_eq!(status.state, ServiceState::Running);
assert_eq!(status.message, "OK");
assert_eq!(status.uptime_seconds, 100);
}
#[test]
fn test_health_status_clone() {
let status = HealthStatus {
state: ServiceState::Degraded,
message: "Warning".to_string(),
last_check: std::time::SystemTime::now(),
uptime_seconds: 50,
metrics: ServiceMetrics::default(),
};
let cloned = status.clone();
assert_eq!(cloned.state, ServiceState::Degraded);
assert_eq!(cloned.message, "Warning");
}
#[test]
fn test_health_status_debug() {
let status = HealthStatus {
state: ServiceState::Running,
message: "OK".to_string(),
last_check: std::time::SystemTime::now(),
uptime_seconds: 0,
metrics: ServiceMetrics::default(),
};
let debug = format!("{:?}", status);
assert!(debug.contains("HealthStatus"));
}
#[test]
fn test_health_status_serialization() {
let status = HealthStatus {
state: ServiceState::Running,
message: "Healthy".to_string(),
last_check: std::time::SystemTime::now(),
uptime_seconds: 1000,
metrics: ServiceMetrics::default(),
};
let json = serde_json::to_string(&status).unwrap();
assert!(json.contains("Running"));
assert!(json.contains("Healthy"));
}
#[test]
fn test_lifecycle_event_started() {
let event = LifecycleEvent::Started;
let debug = format!("{:?}", event);
assert!(debug.contains("Started"));
}
#[test]
fn test_lifecycle_event_stopped() {
let event = LifecycleEvent::Stopped;
let debug = format!("{:?}", event);
assert!(debug.contains("Stopped"));
}
#[test]
fn test_lifecycle_event_health_check_passed() {
let event = LifecycleEvent::HealthCheckPassed;
let debug = format!("{:?}", event);
assert!(debug.contains("HealthCheckPassed"));
}
#[test]
fn test_lifecycle_event_health_check_failed() {
let event = LifecycleEvent::HealthCheckFailed("timeout".to_string());
if let LifecycleEvent::HealthCheckFailed(msg) = &event {
assert_eq!(msg, "timeout");
} else {
panic!("Expected HealthCheckFailed");
}
}
#[test]
fn test_lifecycle_event_state_changed() {
let event = LifecycleEvent::StateChanged(ServiceState::Running);
if let LifecycleEvent::StateChanged(state) = &event {
assert_eq!(*state, ServiceState::Running);
} else {
panic!("Expected StateChanged");
}
}
#[test]
fn test_lifecycle_event_error() {
let event = LifecycleEvent::Error("something went wrong".to_string());
if let LifecycleEvent::Error(msg) = &event {
assert!(msg.contains("something"));
} else {
panic!("Expected Error");
}
}
#[test]
fn test_lifecycle_event_clone() {
let event = LifecycleEvent::Started;
let cloned = event.clone();
assert!(matches!(cloned, LifecycleEvent::Started));
}
#[test]
fn test_lifecycle_event_serialization() {
let event = LifecycleEvent::Stopped;
let json = serde_json::to_string(&event).unwrap();
let deserialized: LifecycleEvent = serde_json::from_str(&json).unwrap();
assert!(matches!(deserialized, LifecycleEvent::Stopped));
}
#[test]
fn test_service_supervisor_new() {
let supervisor = ServiceSupervisor::new();
assert!(!supervisor.running.load(Ordering::Relaxed));
}
#[test]
fn test_service_supervisor_default() {
let supervisor = ServiceSupervisor::default();
assert!(!supervisor.running.load(Ordering::Relaxed));
}
#[tokio::test]
async fn test_service_supervisor_start_stop() {
let supervisor = ServiceSupervisor::new();
let result = supervisor.start_all().await;
assert!(result.is_ok());
assert!(supervisor.running.load(Ordering::Relaxed));
let result = supervisor.stop_all().await;
assert!(result.is_ok());
assert!(!supervisor.running.load(Ordering::Relaxed));
}
#[tokio::test]
async fn test_service_supervisor_get_all_health() {
let supervisor = ServiceSupervisor::new();
let health = supervisor.get_all_health().await;
assert!(health.is_empty()); }
#[tokio::test]
async fn test_lifecycle_wrapper_initial_state() {
let service = TestService {
fail_count: std::sync::atomic::AtomicU32::new(10), };
let wrapper = LifecycleWrapper::new(service, Duration::from_secs(1));
assert_eq!(wrapper.get_state().await, ServiceState::Uninitialized);
}
#[tokio::test]
async fn test_lifecycle_wrapper_health_message() {
let service = TestService {
fail_count: std::sync::atomic::AtomicU32::new(10),
};
let wrapper = LifecycleWrapper::new(service, Duration::from_secs(1));
wrapper.start().await.unwrap();
let health = wrapper.get_health().await;
assert!(health.message.contains("Running"));
wrapper.stop().await.unwrap();
}
#[tokio::test]
async fn test_lifecycle_wrapper_process_updates_metrics() {
let service = TestService {
fail_count: std::sync::atomic::AtomicU32::new(10),
};
let wrapper = LifecycleWrapper::new(service, Duration::from_secs(1));
wrapper.start().await.unwrap();
let _ = wrapper.process("test".to_string()).await;
let health = wrapper.get_health().await;
assert!(health.metrics.request_count >= 1);
wrapper.stop().await.unwrap();
}
}
#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod property_tests {
use proptest::prelude::*;
proptest! {
#[test]
fn basic_property_stability(_input in ".*") {
prop_assert!(true);
}
#[test]
fn module_consistency_check(_x in 0u32..1000) {
prop_assert!(_x < 1001);
}
}
}