injoint/dispatcher/
test.rs

1#[cfg(test)]
2mod tests {
3    use super::*;
4    use crate::dispatcher::{ActionResponse, Dispatchable};
5    use crate::utils::types::{Broadcastable, Receivable};
6    use serde::{Deserialize, Serialize};
7    use std::future::Future;
8
9    #[derive(Debug, Clone, Serialize, Deserialize)]
10    enum TestAction {
11        Increment,
12        Add(i32),
13        Echo(String),
14    }
15
16    impl Receivable for TestAction {}
17
18    #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
19    struct TestState {
20        counter: i32,
21        messages: Vec<String>,
22    }
23
24    impl Broadcastable for TestState {}
25
26    #[derive(Clone, Default)]
27    struct TestReducer {
28        state: TestState,
29    }
30
31    impl Dispatchable for TestReducer {
32        type Action = TestAction;
33        type State = TestState;
34
35        fn dispatch(
36            &mut self,
37            client_id: u64,
38            action: Self::Action,
39        ) -> impl Future<Output = Result<ActionResponse<Self::State>, String>> + Send {
40            async move {
41                match action {
42                    TestAction::Increment => {
43                        self.state.counter += 1;
44                    }
45                    TestAction::Add(value) => {
46                        self.state.counter += value;
47                    }
48                    TestAction::Echo(message) => {
49                        self.state.messages.push(message);
50                    }
51                }
52
53                Ok(ActionResponse {
54                    status: "success".to_string(),
55                    state: self.state.clone(),
56                    author: client_id,
57                    data: "".to_string(),
58                })
59            }
60        }
61
62        fn extern_dispatch(
63            &mut self,
64            client_id: u64,
65            action: &str,
66        ) -> impl Future<Output = Result<ActionResponse<Self::State>, String>> + Send {
67            async move {
68                let action: TestAction = serde_json::from_str(action)
69                    .map_err(|e| format!("Failed to deserialize action: {}", e))?;
70                self.dispatch(client_id, action).await
71            }
72        }
73
74        fn get_state(&self) -> Self::State {
75            self.state.clone()
76        }
77    }
78
79    #[tokio::test]
80    async fn test_action_response_serialization() {
81        let state = TestState {
82            counter: 42,
83            messages: vec!["hello".to_string()],
84        };
85
86        let response = ActionResponse {
87            status: "success".to_string(),
88            state: state.clone(),
89            author: 123,
90            data: "Test data".to_string(),
91        };
92
93        let json = serde_json::to_string(&response).unwrap();
94
95        let deserialized: ActionResponse<TestState> = serde_json::from_str(&json).unwrap();
96        assert_eq!(deserialized.status, "success");
97        assert_eq!(deserialized.state, state);
98        assert_eq!(deserialized.author, 123);
99        assert_eq!(deserialized.data, "Test data");
100    }
101
102    #[tokio::test]
103    async fn test_dispatchable_dispatch() {
104        let mut reducer = TestReducer::default();
105
106        assert_eq!(reducer.get_state().counter, 0);
107        assert_eq!(reducer.get_state().messages.len(), 0);
108
109        let client_id = 1;
110        let result = reducer.dispatch(client_id, TestAction::Increment).await;
111        assert!(result.is_ok());
112        assert_eq!(reducer.get_state().counter, 1);
113
114        let result = reducer.dispatch(client_id, TestAction::Add(10)).await;
115        assert!(result.is_ok());
116        assert_eq!(reducer.get_state().counter, 11);
117
118        let message = "Hello, world!".to_string();
119        let result = reducer
120            .dispatch(client_id, TestAction::Echo(message.clone()))
121            .await;
122        assert!(result.is_ok());
123        assert_eq!(reducer.get_state().messages.len(), 1);
124        assert_eq!(reducer.get_state().messages[0], message);
125
126        let response = result.unwrap();
127        assert_eq!(response.status, "success");
128        assert_eq!(response.author, client_id);
129        assert_eq!(response.state.counter, 11);
130        assert_eq!(response.state.messages.len(), 1);
131    }
132
133    #[tokio::test]
134    async fn test_dispatchable_extern_dispatch() {
135        let mut reducer = TestReducer::default();
136
137        let client_id = 2;
138        let action_json = r#"{"Increment":null}"#;
139        let result = reducer.extern_dispatch(client_id, action_json).await;
140        assert!(result.is_ok());
141        assert_eq!(reducer.get_state().counter, 1);
142
143        let action_json = r#"{"Add":5}"#;
144        let result = reducer.extern_dispatch(client_id, action_json).await;
145        assert!(result.is_ok());
146        assert_eq!(reducer.get_state().counter, 6);
147
148        let action_json = r#"{"Echo":"Hello from JSON"}"#;
149        let result = reducer.extern_dispatch(client_id, action_json).await;
150        assert!(result.is_ok());
151        assert_eq!(reducer.get_state().messages.len(), 1);
152        assert_eq!(reducer.get_state().messages[0], "Hello from JSON");
153
154        let invalid_json = r#"{"InvalidAction":null}"#;
155        let result = reducer.extern_dispatch(client_id, invalid_json).await;
156        assert!(result.is_err());
157    }
158}