mod common;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::time::Duration;
use task_supervisor::{SupervisedTask, SupervisorBuilder, TaskResult, TaskStatus};
use tokio::time::pause;
#[derive(Clone)]
struct StatefulTask {
pub owned_counter: u64,
pub shared_counter: Arc<AtomicUsize>,
pub observed_owned_values: Arc<std::sync::Mutex<Vec<u64>>>,
}
#[allow(clippy::useless_conversion)]
impl SupervisedTask for StatefulTask {
async fn run(&mut self) -> TaskResult {
self.observed_owned_values
.lock()
.unwrap()
.push(self.owned_counter);
self.owned_counter += 1000;
self.shared_counter.fetch_add(1, Ordering::SeqCst);
Err(anyhow::anyhow!("intentional failure").into())
}
}
#[tokio::test]
async fn test_owned_fields_reset_on_restart() {
pause();
let shared_counter = Arc::new(AtomicUsize::new(0));
let observed_owned_values = Arc::new(std::sync::Mutex::new(Vec::new()));
let task = StatefulTask {
owned_counter: 42,
shared_counter: shared_counter.clone(),
observed_owned_values: observed_owned_values.clone(),
};
let handle = SupervisorBuilder::new()
.with_max_restart_attempts(3)
.with_base_restart_delay(Duration::from_millis(50))
.with_max_backoff_exponent(0)
.build()
.run();
handle.add_task("stateful", task).unwrap();
tokio::time::sleep(Duration::from_millis(500)).await;
let status = handle.get_task_status("stateful").await.unwrap().unwrap();
assert_eq!(status, TaskStatus::Dead);
assert_eq!(shared_counter.load(Ordering::SeqCst), 4);
let observed = observed_owned_values.lock().unwrap();
assert_eq!(observed.len(), 4);
for (i, &value) in observed.iter().enumerate() {
assert_eq!(
value, 42,
"Run {i} saw owned_counter={value}, expected 42 (original). \
Owned state leaked across restarts."
);
}
}
#[tokio::test]
async fn test_shared_state_persists_across_restarts() {
pause();
let shared_counter = Arc::new(AtomicUsize::new(0));
let task = StatefulTask {
owned_counter: 0,
shared_counter: shared_counter.clone(),
observed_owned_values: Arc::new(std::sync::Mutex::new(Vec::new())),
};
let handle = SupervisorBuilder::new()
.with_max_restart_attempts(5)
.with_base_restart_delay(Duration::from_millis(50))
.with_max_backoff_exponent(0)
.build()
.run();
handle.add_task("shared_state", task).unwrap();
tokio::time::sleep(Duration::from_millis(500)).await;
let status = handle
.get_task_status("shared_state")
.await
.unwrap()
.unwrap();
assert_eq!(status, TaskStatus::Dead);
assert_eq!(
shared_counter.load(Ordering::SeqCst),
6,
"Shared (Arc) state should accumulate across all runs"
);
}