injoint/dispatcher/
test.rs1#[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}