simple-actors/tests/errors.rs
Blake Rain bb7bb6b712
Some checks failed
Check / check (push) Failing after 41s
feat: add some new features
2024-04-06 12:10:25 +01:00

119 lines
3.6 KiB
Rust

use async_trait::async_trait;
use simple_actors::{messages, Actor, Context, Handler, SendError};
use test_log::test;
#[derive(Default)]
struct ErrorActor {
count: usize,
}
#[derive(Debug)]
enum Error {
Recoverable,
Irrecoverable,
}
impl Actor for ErrorActor {
type Error = Error;
fn is_recoverable(&mut self, error: &Self::Error) -> bool {
matches!(error, Error::Recoverable)
}
}
enum Ping {
Regular,
Error(Error),
}
messages! {
Ping => usize
}
#[async_trait]
impl Handler<Ping> for ErrorActor {
async fn handle(&mut self, ping: Ping) -> Result<usize, Self::Error> {
match ping {
Ping::Regular => {
// Regular ping operation
self.count += 1;
Ok(self.count)
}
Ping::Error(err) => {
// Irregular ping operation
Err(err)
}
}
}
}
#[test(tokio::test)]
async fn test_actor_error_recoverable() -> Result<(), Box<dyn std::error::Error>> {
// Create the context and spawn our ping actor.
let ctx = Context::default();
let hdl = ctx.spawn_default::<ErrorActor>().await;
// Send a ping message to the actor as normal
let res = hdl.send(Ping::Regular).await?;
assert_eq!(res, 1);
// Now send a message to the actor that causes the actor to encounter an error. This is not an
// irrecoverable error, so the actor should still be alive afterwards.
//
// This should result in an error indicating that the reply channel has closed. This will be
// because the actor has handled the message, but then encountered an error. The error caused
// the `EnvelopeHandler::handle` method to just drop the oneshot reply sender.
let res = hdl.send(Ping::Error(Error::Recoverable)).await;
assert_eq!(res, Err(SendError::ReplyChannelClosed));
// Make sure that our actor is still alive and well
let res = hdl.send(Ping::Regular).await?;
assert_eq!(res, 2);
// Make sure that we still have an actor left
assert_eq!(ctx.remaining_actors().await, 1);
// We have to drop the handle here, or `ctx.wait_all` will not return.
drop(hdl);
ctx.wait_all().await;
Ok(())
}
#[test(tokio::test)]
async fn test_actor_error_irrecoverable() -> Result<(), Box<dyn std::error::Error>> {
// Create the context and spawn our ping actor.
let ctx = Context::default();
let hdl = ctx.spawn_default::<ErrorActor>().await;
// Send a ping message to the actor as normal
let res = hdl.send(Ping::Regular).await?;
assert_eq!(res, 1);
// Now send a message that causes the actor to terminate it's event loop. This is done with a
// ping message that tells the actor's handler for this message to return an error. The error
// is irrecoverable, meaning the actor will end.
//
// This should result in an error indicating that the reply channel has closed. This will be
// because the actor has handled the message, but then encountered an error. The error caused
// the `EnvelopeHandler::handle` method to just drop the oneshot reply sender.
let res = hdl.send(Ping::Error(Error::Irrecoverable)).await;
assert_eq!(res, Err(SendError::ReplyChannelClosed));
// Make sure that we have no more actors left
assert_eq!(ctx.remaining_actors().await, 0);
// Attempting to use the handle again should result in an error
let res = hdl.send(Ping::Regular).await;
assert_eq!(res, Err(SendError::ChannelClosed));
// We have to drop the handle here, or `ctx.wait_all` will not return.
drop(hdl);
ctx.wait_all().await;
Ok(())
}