tsuzuri 0.1.285

Tsuzuri is a Event Sourcing framework for Rust, designed to be simple and easy to use.
Documentation
use crate::{event::Envelope, integration::error::Result, integration_event::IntegrationEvent};
use async_trait::async_trait;

pub trait Adapter<E: IntegrationEvent>: Executer<E> + Send + Sync + 'static {}

impl<D, E> Adapter<E> for D
where
    D: Executer<E> + Send + Sync + 'static,
    E: IntegrationEvent,
{
}

#[async_trait]
pub trait Executer<E: IntegrationEvent>: Send + Sync + 'static {
    async fn execute(&mut self, event: Envelope<E>) -> Result<()>;
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::message::{self, Metadata};
    use async_trait::async_trait;
    use std::sync::{Arc, Mutex};

    #[derive(Debug, Clone, PartialEq)]
    struct TestIntegrationEvent {
        pub id: String,
        pub data: String,
    }

    impl message::Message for TestIntegrationEvent {
        fn name(&self) -> &'static str {
            "TestIntegrationEvent"
        }
    }

    impl IntegrationEvent for TestIntegrationEvent {
        fn id(&self) -> String {
            self.id.clone()
        }

        fn event_type(&self) -> &'static str {
            "TestIntegrationEvent"
        }
    }

    #[derive(Clone)]
    struct MockExecuter {
        called: Arc<Mutex<Vec<TestIntegrationEvent>>>,
        should_fail: bool,
    }

    impl MockExecuter {
        fn new(should_fail: bool) -> Self {
            Self {
                called: Arc::new(Mutex::new(Vec::new())),
                should_fail,
            }
        }

        fn get_calls(&self) -> Vec<TestIntegrationEvent> {
            self.called.lock().unwrap().clone()
        }
    }

    #[async_trait]
    impl Executer<TestIntegrationEvent> for MockExecuter {
        async fn execute(&mut self, event: Envelope<TestIntegrationEvent>) -> Result<()> {
            if self.should_fail {
                return Err(crate::integration::error::IntegrationError::Database(
                    "Mock execution failed".to_string(),
                ));
            }
            self.called.lock().unwrap().push(event.message);
            Ok(())
        }
    }

    #[tokio::test]
    async fn test_executer_success() {
        let mut executer = MockExecuter::new(false);
        let event = TestIntegrationEvent {
            id: "test-id".to_string(),
            data: "test-data".to_string(),
        };
        let envelope = Envelope {
            message: event.clone(),
            metadata: Metadata::default(),
        };

        let result = executer.execute(envelope).await;
        assert!(result.is_ok());

        let calls = executer.get_calls();
        assert_eq!(calls.len(), 1);
        assert_eq!(calls[0], event);
    }

    #[tokio::test]
    async fn test_executer_failure() {
        let mut executer = MockExecuter::new(true);
        let event = TestIntegrationEvent {
            id: "test-id".to_string(),
            data: "test-data".to_string(),
        };
        let envelope = Envelope {
            message: event,
            metadata: Metadata::default(),
        };

        let result = executer.execute(envelope).await;
        assert!(result.is_err());
        match result {
            Err(crate::integration::error::IntegrationError::Database(msg)) => {
                assert_eq!(msg, "Mock execution failed");
            }
            _ => panic!("Expected Database error"),
        }

        let calls = executer.get_calls();
        assert_eq!(calls.len(), 0);
    }

    #[test]
    fn test_adapter_trait_impl() {
        let executer = MockExecuter::new(false);

        fn assert_adapter<T: Adapter<TestIntegrationEvent>>(_: &T) {}

        assert_adapter(&executer);
    }
}