use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use anyhow::Result;
use async_trait::async_trait;
use bytes::Bytes;
use folk_api::{Executor, Plugin, PluginContext};
use folk_core::plugin_registry::PluginRegistry;
use tokio::sync::watch;
static SEQ: AtomicUsize = AtomicUsize::new(0);
struct TrackingPlugin {
name: &'static str,
boot_order: Arc<AtomicUsize>,
shutdown_order: Arc<AtomicUsize>,
}
#[async_trait]
impl Plugin for TrackingPlugin {
fn name(&self) -> &'static str {
self.name
}
async fn boot(&mut self, _: PluginContext) -> Result<()> {
self.boot_order
.store(SEQ.fetch_add(1, Ordering::SeqCst), Ordering::SeqCst);
Ok(())
}
async fn shutdown(&self) -> Result<()> {
self.shutdown_order
.store(SEQ.fetch_add(1, Ordering::SeqCst), Ordering::SeqCst);
Ok(())
}
}
struct StubExecutor;
#[async_trait]
impl Executor for StubExecutor {
async fn execute_method(&self, _method: &str, _payload: Bytes) -> Result<Bytes> {
unimplemented!()
}
}
#[tokio::test]
async fn boots_in_order_and_shuts_in_reverse() {
SEQ.store(0, Ordering::SeqCst);
let a_b = Arc::new(AtomicUsize::new(0));
let a_s = Arc::new(AtomicUsize::new(0));
let b_b = Arc::new(AtomicUsize::new(0));
let b_s = Arc::new(AtomicUsize::new(0));
let mut reg = PluginRegistry::new();
reg.register(Box::new(TrackingPlugin {
name: "a",
boot_order: a_b.clone(),
shutdown_order: a_s.clone(),
}));
reg.register(Box::new(TrackingPlugin {
name: "b",
boot_order: b_b.clone(),
shutdown_order: b_s.clone(),
}));
let (_tx, rx) = watch::channel(false);
let ctx = PluginContext {
executor: Arc::new(StubExecutor),
shutdown: rx,
rpc_registrar: None,
health_registry: None,
metrics_registry: None,
};
reg.boot_all(&ctx).await.unwrap();
assert!(a_b.load(Ordering::SeqCst) < b_b.load(Ordering::SeqCst));
reg.shutdown_all().await;
assert!(b_s.load(Ordering::SeqCst) < a_s.load(Ordering::SeqCst));
}