use std::sync::Arc;
use anyhow::Result;
use async_trait::async_trait;
use bytes::Bytes;
use folk_api::{Executor, Plugin, PluginContext, RpcMethodDef};
use tokio::sync::watch;
struct StubExecutor;
#[async_trait]
impl Executor for StubExecutor {
async fn execute_method(&self, _: &str, payload: Bytes) -> Result<Bytes> {
Ok(payload)
}
}
struct DemoPlugin {
booted: bool,
}
#[async_trait]
impl Plugin for DemoPlugin {
fn name(&self) -> &'static str {
"demo"
}
async fn boot(&mut self, _ctx: PluginContext) -> Result<()> {
self.booted = true;
Ok(())
}
async fn shutdown(&self) -> Result<()> {
Ok(())
}
fn rpc_methods(&self) -> Vec<RpcMethodDef> {
vec![RpcMethodDef::new("demo.ping", "ping the demo plugin")]
}
}
#[tokio::test]
async fn plugin_boot_and_executor_round_trip() {
let (_tx, rx) = watch::channel(false);
let mut plugin = DemoPlugin { booted: false };
let executor: Arc<dyn Executor> = Arc::new(StubExecutor);
let ctx = PluginContext {
executor: executor.clone(),
shutdown: rx,
rpc_registrar: None,
health_registry: None,
metrics_registry: None,
};
plugin.boot(ctx).await.unwrap();
assert!(plugin.booted);
let payload = Bytes::from("hello");
let response = executor.execute(payload.clone()).await.unwrap();
assert_eq!(response, payload);
}
#[test]
fn folk_api_version_is_set() {
assert!(!folk_api::FOLK_API_VERSION.is_empty());
assert!(folk_api::FOLK_API_VERSION.starts_with("0."));
}
#[tokio::test]
async fn execute_direct_default_returns_error() {
let executor: Arc<dyn Executor> = Arc::new(StubExecutor);
let request: String = "test".to_string();
let result = executor.execute_direct("test", &request).await;
assert!(result.is_err(), "default execute_direct should return Err");
}
#[tokio::test]
async fn execute_direct_custom_impl_works() {
struct DirectExecutor;
#[async_trait]
impl Executor for DirectExecutor {
async fn execute_method(&self, _: &str, _: Bytes) -> Result<Bytes> {
Ok(Bytes::new())
}
async fn execute_direct(
&self,
method: &str,
request: &(dyn std::any::Any + Send + Sync),
) -> Result<Box<dyn std::any::Any + Send>> {
let s = request.downcast_ref::<String>().unwrap();
Ok(Box::new(format!("{method}:{s}")))
}
}
let executor: Arc<dyn Executor> = Arc::new(DirectExecutor);
let request = "hello".to_string();
let result = executor.execute_direct("greet", &request).await.unwrap();
let response = result.downcast::<String>().unwrap();
assert_eq!(*response, "greet:hello");
}