#![allow(clippy::unwrap_used, clippy::expect_used)]
use actor_kit::{
ActorBuilder, ActorScheduler, ChildSpec, ExitReason, Message, MessagePayload, RestartPolicy,
SchedulerConfig, SupervisionStrategy, SupervisorTree,
};
use std::sync::Arc;
use std::time::Duration;
#[tokio::test]
async fn crash_then_supervised_restart_then_roundtrip() {
let scheduler = Arc::new(ActorScheduler::new(SchedulerConfig::new().workers(2)));
scheduler.start().unwrap();
let mut tree =
SupervisorTree::new(SupervisionStrategy::one_for_one(3, Duration::from_secs(60)));
let root = tree.root();
let spec = ChildSpec::new("worker-1").restart_policy(RestartPolicy::Permanent);
let spec_name = spec.name.clone();
let _supervised_id = tree.start_child_under(root, spec).unwrap();
let handle = ActorBuilder::new()
.name(spec_name.as_str())
.spawn(&scheduler)
.unwrap();
scheduler.set_actor_running(&handle.id()).unwrap();
tokio::time::sleep(Duration::from_millis(10)).await; scheduler
.send(
handle.id(),
Message {
sender: None,
payload: MessagePayload::Custom(vec![1]),
priority: actor_kit::Priority::Normal,
},
)
.await
.unwrap();
let deadline = std::time::Instant::now() + Duration::from_secs(5);
while scheduler.registry().get_processed_count(&handle.id()) < 1 {
assert!(
std::time::Instant::now() < deadline,
"no message processed in time"
);
tokio::time::sleep(Duration::from_millis(5)).await;
}
assert_eq!(scheduler.registry().get_processed_count(&handle.id()), 1);
scheduler
.registry()
.set_state(&handle.id(), actor_kit::ActorState::Failed)
.unwrap();
let dead = scheduler
.send(
handle.id(),
Message {
sender: None,
payload: MessagePayload::Custom(vec![2]),
priority: actor_kit::Priority::Normal,
},
)
.await;
assert!(dead.is_err(), "send to crashed actor should fail");
tree.handle_child_exit(
root,
&spec_name,
ExitReason::Error("panic: worker exploded".into()),
)
.await
.unwrap();
let restarted = tree
.get_supervisor(&root)
.unwrap()
.get_child(&spec_name)
.unwrap()
.id;
assert_ne!(restarted, handle.id(), "restart must mint a new actor ID");
assert_eq!(
tree.get_supervisor(&root)
.unwrap()
.count_children()
.total_restarts,
1
);
let new_handle = ActorBuilder::new()
.name(format!("{spec_name}-v2"))
.spawn(&scheduler)
.unwrap();
scheduler.set_actor_running(&new_handle.id()).unwrap();
scheduler
.send(
new_handle.id(),
Message {
sender: None,
payload: MessagePayload::Custom(vec![3]),
priority: actor_kit::Priority::Normal,
},
)
.await
.unwrap();
let deadline = std::time::Instant::now() + Duration::from_secs(5);
while scheduler.registry().get_processed_count(&new_handle.id()) < 1 {
assert!(
std::time::Instant::now() < deadline,
"restarted actor processed nothing"
);
tokio::time::sleep(Duration::from_millis(5)).await;
}
assert!(new_handle.is_running());
scheduler.stop();
}