Skip to main content

ranvier_runtime/
testkit.rs

1use crate::Axon;
2use ranvier_core::{Bus, Outcome, transition::ResourceRequirement};
3use std::any::Any;
4
5/// Lightweight helper for Axon unit tests.
6///
7/// Provides explicit Bus setup and resource injection so tests can mock
8/// dependencies without hidden framework wiring.
9pub struct AxonTestKit<R> {
10    resources: R,
11    bus: Bus,
12}
13
14impl<R> AxonTestKit<R> {
15    pub fn new(resources: R) -> Self {
16        Self {
17            resources,
18            bus: Bus::new(),
19        }
20    }
21
22    pub fn with_bus(resources: R, bus: Bus) -> Self {
23        Self { resources, bus }
24    }
25
26    pub fn insert<T: Any + Send + Sync + 'static>(&mut self, value: T) -> &mut Self {
27        self.bus.insert(value);
28        self
29    }
30
31    pub fn bus(&self) -> &Bus {
32        &self.bus
33    }
34
35    pub fn bus_mut(&mut self) -> &mut Bus {
36        &mut self.bus
37    }
38
39    pub fn resources(&self) -> &R {
40        &self.resources
41    }
42
43    pub fn resources_mut(&mut self) -> &mut R {
44        &mut self.resources
45    }
46
47    pub fn into_parts(self) -> (R, Bus) {
48        (self.resources, self.bus)
49    }
50}
51
52impl<R> AxonTestKit<R>
53where
54    R: ResourceRequirement,
55{
56    pub async fn run<In, Out, E>(
57        &mut self,
58        axon: &Axon<In, Out, E, R>,
59        input: In,
60    ) -> Outcome<Out, E>
61    where
62        In: Send + Sync + 'static,
63        Out: Send + Sync + 'static,
64        E: Send + 'static,
65    {
66        axon.execute(input, &self.resources, &mut self.bus).await
67    }
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73    use ranvier_core::{Outcome, Transition};
74    use std::convert::Infallible;
75
76    #[derive(Clone)]
77    struct MockResources {
78        offset: usize,
79    }
80
81    impl ranvier_core::transition::ResourceRequirement for MockResources {}
82
83    #[derive(Clone)]
84    struct SumWithBus;
85
86    #[async_trait::async_trait]
87    impl Transition<usize, usize> for SumWithBus {
88        type Error = Infallible;
89        type Resources = MockResources;
90
91        async fn run(
92            &self,
93            state: usize,
94            resources: &Self::Resources,
95            bus: &mut Bus,
96        ) -> Outcome<usize, Self::Error> {
97            let bus_value = bus.read::<usize>().copied().unwrap_or_default();
98            Outcome::next(state + resources.offset + bus_value)
99        }
100    }
101
102    #[tokio::test]
103    async fn testkit_executes_with_mocked_resources_and_bus_values() {
104        let axon = Axon::<usize, usize, Infallible, MockResources>::new("Sum").then(SumWithBus);
105        let mut kit = AxonTestKit::new(MockResources { offset: 7 });
106        kit.insert(5usize);
107
108        let result = kit.run(&axon, 10).await;
109        match result {
110            Outcome::Next(value) => assert_eq!(value, 22),
111            _ => panic!("expected Outcome::Next"),
112        }
113    }
114}