Skip to main content

opentalk_roomserver_types_chat/
command.rs

1// SPDX-FileCopyrightText: OpenTalk GmbH <mail@opentalk.eu>
2//
3// SPDX-License-Identifier: EUPL-1.2
4
5use opentalk_roomserver_signaling::signaling_module::CreateReplica;
6use opentalk_types_common::time::Timestamp;
7use serde::{Deserialize, Serialize};
8
9use crate::{Scope, event::ChatEvent};
10
11/// Commands for the `chat` namespace
12#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
13#[serde(tag = "action", rename_all = "snake_case")]
14pub enum ChatCommand {
15    /// Enable chat messaging
16    EnableChat,
17
18    /// Disable chat messaging
19    DisableChat,
20
21    /// Send chat message
22    SendMessage {
23        /// The content of the message
24        content: String,
25
26        /// The scope of the message
27        #[serde(flatten)]
28        scope: Scope,
29    },
30
31    /// Get a chunk of the chat history
32    GetHistoryChunk {
33        /// Determines which [`ChatChunk`](crate::state::ChatChunk) is requested.
34        /// This is always the newest message of the chunk.
35        message_index: u32,
36
37        /// The scope of the chat history
38        #[serde(flatten)]
39        scope: Scope,
40    },
41
42    /// Clear chat history
43    ClearHistory,
44
45    /// Set last seen timestamp
46    SetLastSeenTimestamp {
47        /// Scope of the timestamp
48        #[serde(flatten)]
49        scope: Scope,
50
51        /// Last seen timestamp
52        timestamp: Timestamp,
53    },
54
55    /// Search in the history
56    SearchHistory {
57        /// The scope to search in
58        #[serde(flatten)]
59        scope: Scope,
60        /// The search term
61        term: String,
62        /// The message index of the [`ChatChunk`](crate::state::ChatChunk) in the
63        /// search history. Retrieves the latest [`ChatChunk`](crate::state::ChatChunk)
64        /// when [`None`].
65        message_index: Option<u32>,
66    },
67}
68
69impl CreateReplica<ChatEvent> for ChatCommand {
70    fn replicate(&self) -> Option<ChatEvent> {
71        None
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use insta::assert_snapshot;
78    use opentalk_types_common::time::Timestamp;
79    use opentalk_types_signaling::ParticipantId;
80    use pretty_assertions::assert_eq;
81    use serde_json::json;
82
83    use super::*;
84    use crate::Scope;
85
86    #[test]
87    fn serialize_enable_chat() {
88        let command = ChatCommand::EnableChat;
89        let serialized = serde_json::to_value(&command).expect("Serialization failed");
90        assert_eq!(
91            serialized,
92            json!(
93                {
94                    "action":"enable_chat"
95                }
96            )
97        );
98    }
99
100    #[test]
101    fn deserialize_enable_chat() {
102        let json_data = json!(
103            {
104                "action":"enable_chat"
105            }
106        );
107        let deserialized: ChatCommand =
108            serde_json::from_value(json_data).expect("Deserialization failed");
109        assert_eq!(deserialized, ChatCommand::EnableChat);
110    }
111
112    #[test]
113    fn serialize_set_last_seen_timestamp() {
114        let command = ChatCommand::SetLastSeenTimestamp {
115            scope: Scope::Global,
116            timestamp: Timestamp::unix_epoch(),
117        };
118        let serialized = serde_json::to_value(&command).expect("Serialization failed");
119        let expected = json!(
120            {
121                "action":"set_last_seen_timestamp",
122                "scope":"global",
123                "timestamp":"1970-01-01T00:00:00Z"
124            }
125        );
126        assert_eq!(serialized, expected);
127    }
128
129    #[test]
130    fn deserialize_set_last_seen_timestamp() {
131        let json_data = json!(
132            {
133                "action":"set_last_seen_timestamp",
134                "scope":"global",
135                "timestamp":"1970-01-01T00:00:00Z"
136            }
137        );
138        let deserialized: ChatCommand =
139            serde_json::from_value(json_data).expect("Deserialization failed");
140        if let ChatCommand::SetLastSeenTimestamp { scope, .. } = deserialized {
141            assert_eq!(scope, Scope::Global);
142        } else {
143            panic!("Expected ChatCommand::SetLastSeenTimestamp");
144        }
145    }
146
147    #[test]
148    fn serialize_global_message() {
149        let command = ChatCommand::SendMessage {
150            content: "test message".to_string(),
151            scope: Scope::Global,
152        };
153        let serialized = serde_json::to_value(&command).expect("Serialization failed");
154        let expected = json!(
155            {
156                "action":"send_message",
157                "content":"test message",
158                "scope":"global"
159            }
160        );
161        assert_eq!(serialized, expected);
162    }
163
164    #[test]
165    fn deserialize_global_message() {
166        let json_data = json!(
167            {
168                "action":"send_message",
169                "scope":"global",
170                "content":"Hello!"
171            }
172        );
173
174        let deserialized: ChatCommand =
175            serde_json::from_value(json_data).expect("Deserialization failed");
176        if let ChatCommand::SendMessage { content, scope } = deserialized {
177            assert_eq!(content, "Hello!");
178            assert_eq!(scope, Scope::Global);
179        } else {
180            panic!("Expected ChatCommand::SendMessage");
181        }
182    }
183
184    #[test]
185    fn serialize_breakout_message() {
186        let command = ChatCommand::SendMessage {
187            content: "test message".to_string(),
188            scope: Scope::Breakout(0.into()),
189        };
190        let raw = serde_json::to_string_pretty(&command).expect("Serialization failed");
191
192        assert_snapshot!(raw, @r#"
193        {
194          "action": "send_message",
195          "content": "test message",
196          "scope": "breakout",
197          "target": 0
198        }
199        "#);
200    }
201
202    #[test]
203    fn deserialize_breakout_message() {
204        let json = json!({
205            "action": "send_message",
206            "scope": "breakout",
207            "target": 0,
208            "content": "test message"
209        });
210        let msg: ChatCommand = serde_json::from_value(json).unwrap();
211
212        assert_eq!(
213            msg,
214            ChatCommand::SendMessage {
215                content: "test message".to_string(),
216                scope: Scope::Breakout(0.into())
217            }
218        );
219    }
220
221    #[test]
222    fn serialize_private_message() {
223        let command = ChatCommand::SendMessage {
224            content: "test message".to_string(),
225            scope: Scope::Private(ParticipantId::from_u128(1)),
226        };
227        let serialized = serde_json::to_value(&command).expect("Serialization failed");
228
229        let expected = json!(
230            {
231                "action":"send_message",
232                "content":"test message",
233                "scope":"private",
234                "target":"00000000-0000-0000-0000-000000000001"}
235        );
236
237        assert_eq!(serialized, expected);
238    }
239
240    #[test]
241    fn deserialize_private_message() {
242        let json = json!({
243            "action": "send_message",
244            "scope": "private",
245            "target": "00000000-0000-0000-0000-000000000000",
246            "content": "Hello Bob!"
247        });
248
249        let msg: ChatCommand = serde_json::from_value(json).unwrap();
250
251        if let ChatCommand::SendMessage { content, scope } = msg {
252            assert_eq!(scope, Scope::Private(ParticipantId::nil()));
253            assert_eq!(content, "Hello Bob!");
254        } else {
255            panic!()
256        }
257    }
258
259    #[test]
260    fn serialize_get_history_chunk() {
261        let command = ChatCommand::GetHistoryChunk {
262            message_index: 1,
263            scope: Scope::Global,
264        };
265        let produced = serde_json::to_string_pretty(&command).expect("Serialization failed");
266
267        assert_snapshot!(produced, @r#"
268        {
269          "action": "get_history_chunk",
270          "message_index": 1,
271          "scope": "global"
272        }
273        "#);
274    }
275
276    #[test]
277    fn deserialize_get_history_chunk() {
278        let json = json!({
279            "action": "get_history_chunk",
280            "message_index": 1,
281            "scope": "global",
282        });
283
284        let msg: ChatCommand = serde_json::from_value(json).unwrap();
285
286        assert_eq!(
287            msg,
288            ChatCommand::GetHistoryChunk {
289                message_index: 1,
290                scope: Scope::Global
291            }
292        )
293    }
294
295    #[test]
296    fn serialize_search_history() {
297        let command = ChatCommand::SearchHistory {
298            scope: Scope::Global,
299            term: "hello".into(),
300            message_index: None,
301        };
302
303        let produced = serde_json::to_value(&command).unwrap();
304        let expected = json!({
305            "action": "search_history",
306            "scope": "global",
307            "term": "hello",
308            "message_index": null
309        });
310
311        assert_eq!(produced, expected);
312    }
313
314    #[test]
315    fn deserialize_search_history() {
316        let json = json!({
317            "action": "search_history",
318            "scope": "global",
319            "term": "hello",
320            "message_index": null,
321        });
322
323        let msg: ChatCommand = serde_json::from_value(json).unwrap();
324
325        assert_eq!(
326            msg,
327            ChatCommand::SearchHistory {
328                scope: Scope::Global,
329                term: "hello".into(),
330                message_index: None,
331            }
332        );
333    }
334}