use std::future::Future;
use std::pin::Pin;
use super::Event;
pub type SubscriberFuture<'a> = Pin<Box<dyn Future<Output = ()> + Send + 'a>>;
pub trait EventSubscriber: Send + Sync {
fn name(&self) -> &str;
fn handle<'a>(&'a self, event: &'a Event) -> SubscriberFuture<'a>;
}
#[cfg(test)]
mod tests {
use super::*;
struct TestSubscriber {
name: String,
}
impl EventSubscriber for TestSubscriber {
fn name(&self) -> &str {
&self.name
}
fn handle<'a>(&'a self, _event: &'a Event) -> SubscriberFuture<'a> {
Box::pin(async move {
})
}
}
struct CountingSubscriber {
name: String,
}
impl EventSubscriber for CountingSubscriber {
fn name(&self) -> &str {
&self.name
}
fn handle<'a>(&'a self, _event: &'a Event) -> SubscriberFuture<'a> {
Box::pin(async move {
tokio::time::sleep(tokio::time::Duration::from_millis(1)).await;
})
}
}
#[test]
fn subscriber_has_identifier_name() {
let sub = TestSubscriber {
name: "test_sub".to_string(),
};
assert_eq!(sub.name(), "test_sub");
}
#[test]
fn subscriber_name_is_consistent() {
let sub = TestSubscriber {
name: "my_subscriber".to_string(),
};
assert_eq!(sub.name(), "my_subscriber");
assert_eq!(sub.name(), "my_subscriber");
}
#[test]
fn different_subscribers_have_different_names() {
let sub1 = TestSubscriber {
name: "sub1".to_string(),
};
let sub2 = TestSubscriber {
name: "sub2".to_string(),
};
assert_ne!(sub1.name(), sub2.name());
}
#[tokio::test]
async fn subscriber_handle_completes_successfully() {
use chrono::Utc;
let sub = TestSubscriber {
name: "test".to_string(),
};
let event = Event::RunCreated {
run_id: uuid::Uuid::now_v7(),
workflow_name: "test-wf".to_string(),
at: Utc::now(),
};
sub.handle(&event).await;
}
#[tokio::test]
async fn subscriber_handle_is_async() {
use chrono::Utc;
let sub = CountingSubscriber {
name: "async_test".to_string(),
};
let event = Event::RunCreated {
run_id: uuid::Uuid::now_v7(),
workflow_name: "test".to_string(),
at: Utc::now(),
};
let start = std::time::Instant::now();
sub.handle(&event).await;
let elapsed = start.elapsed();
assert!(elapsed.as_millis() >= 1);
}
#[tokio::test]
async fn multiple_subscribers_can_handle_same_event() {
use chrono::Utc;
let sub1 = TestSubscriber {
name: "sub1".to_string(),
};
let sub2 = TestSubscriber {
name: "sub2".to_string(),
};
let event = Event::RunCreated {
run_id: uuid::Uuid::now_v7(),
workflow_name: "test".to_string(),
at: Utc::now(),
};
sub1.handle(&event).await;
sub2.handle(&event).await;
}
#[test]
fn subscriber_implements_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<TestSubscriber>();
assert_send_sync::<CountingSubscriber>();
}
#[tokio::test]
async fn subscriber_future_is_boxed() {
use chrono::Utc;
let sub = TestSubscriber {
name: "boxed_test".to_string(),
};
let event = Event::RunCreated {
run_id: uuid::Uuid::now_v7(),
workflow_name: "test".to_string(),
at: Utc::now(),
};
let future = sub.handle(&event);
let _ = future.await;
}
#[test]
fn subscriber_name_borrowed_lifetime() {
let sub = TestSubscriber {
name: "lifetime_test".to_string(),
};
let name1 = sub.name();
let name2 = sub.name();
assert_eq!(name1, name2);
assert_eq!(name1, "lifetime_test");
}
}