tlq_client/
message.rs

1use serde::{Deserialize, Serialize};
2use uuid::Uuid;
3
4#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5pub struct Message {
6    pub id: Uuid,
7    pub body: String,
8    pub state: MessageState,
9    #[serde(skip_serializing_if = "Option::is_none")]
10    pub lock_until: Option<String>, // ISO datetime string
11    pub retry_count: u32,
12}
13
14#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
15#[serde(rename_all = "PascalCase")]
16pub enum MessageState {
17    Ready,
18    Processing,
19    Failed,
20}
21
22impl Message {
23    pub fn new(body: String) -> Self {
24        Self {
25            id: Uuid::now_v7(),
26            body,
27            state: MessageState::Ready,
28            lock_until: None,
29            retry_count: 0,
30        }
31    }
32}
33
34#[derive(Debug, Serialize)]
35pub struct AddMessageRequest {
36    pub body: String,
37}
38
39#[derive(Debug, Serialize)]
40pub struct GetMessagesRequest {
41    pub count: u32,
42}
43
44#[derive(Debug, Serialize)]
45pub struct DeleteMessagesRequest {
46    pub ids: Vec<Uuid>,
47}
48
49#[derive(Debug, Serialize)]
50pub struct RetryMessagesRequest {
51    pub ids: Vec<Uuid>,
52}
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57    use serde_json;
58
59    #[test]
60    fn test_message_creation() {
61        let message = Message::new("Test message".to_string());
62
63        assert_eq!(message.body, "Test message");
64        assert_eq!(message.state, MessageState::Ready);
65        assert_eq!(message.retry_count, 0);
66
67        // UUID should be valid
68        assert!(!message.id.to_string().is_empty());
69    }
70
71    #[test]
72    fn test_message_state_serialization() {
73        // Test that MessageState serializes to the expected Pascal case
74        assert_eq!(
75            serde_json::to_string(&MessageState::Ready).unwrap(),
76            "\"Ready\""
77        );
78        assert_eq!(
79            serde_json::to_string(&MessageState::Processing).unwrap(),
80            "\"Processing\""
81        );
82        assert_eq!(
83            serde_json::to_string(&MessageState::Failed).unwrap(),
84            "\"Failed\""
85        );
86    }
87
88    #[test]
89    fn test_message_state_deserialization() {
90        // Test that MessageState deserializes from Pascal case
91        assert_eq!(
92            serde_json::from_str::<MessageState>("\"Ready\"").unwrap(),
93            MessageState::Ready
94        );
95        assert_eq!(
96            serde_json::from_str::<MessageState>("\"Processing\"").unwrap(),
97            MessageState::Processing
98        );
99        assert_eq!(
100            serde_json::from_str::<MessageState>("\"Failed\"").unwrap(),
101            MessageState::Failed
102        );
103    }
104
105    #[test]
106    fn test_message_state_invalid_deserialization() {
107        // Test that invalid states fail to deserialize
108        let result = serde_json::from_str::<MessageState>("\"Invalid\"");
109        assert!(result.is_err());
110
111        let result = serde_json::from_str::<MessageState>("\"ready\""); // lowercase
112        assert!(result.is_err());
113
114        let result = serde_json::from_str::<MessageState>("\"READY\""); // uppercase
115        assert!(result.is_err());
116    }
117
118    #[test]
119    fn test_message_serialization() {
120        let message = Message::new("test body".to_string());
121
122        let json = serde_json::to_string(&message).unwrap();
123
124        // Should contain all fields
125        assert!(json.contains("\"id\":"));
126        assert!(json.contains("\"body\":\"test body\""));
127        assert!(json.contains("\"state\":\"Ready\""));
128        assert!(json.contains("\"retry_count\":0"));
129
130        // Should deserialize back correctly
131        let deserialized: Message = serde_json::from_str(&json).unwrap();
132        assert_eq!(deserialized.body, message.body);
133        assert_eq!(deserialized.state, message.state);
134        assert_eq!(deserialized.retry_count, message.retry_count);
135        assert_eq!(deserialized.id, message.id);
136    }
137
138    #[test]
139    fn test_message_with_special_characters() {
140        let special_body = "Test with 🦀 emojis and \"quotes\" and \n newlines \t tabs";
141        let message = Message::new(special_body.to_string());
142
143        assert_eq!(message.body, special_body);
144
145        // Should serialize and deserialize correctly
146        let json = serde_json::to_string(&message).unwrap();
147        let deserialized: Message = serde_json::from_str(&json).unwrap();
148        assert_eq!(deserialized.body, special_body);
149    }
150
151    #[test]
152    fn test_message_with_very_long_body() {
153        let long_body = "a".repeat(100_000);
154        let message = Message::new(long_body.clone());
155
156        assert_eq!(message.body, long_body);
157        assert_eq!(message.body.len(), 100_000);
158    }
159
160    #[test]
161    fn test_message_with_empty_body() {
162        let message = Message::new("".to_string());
163
164        assert_eq!(message.body, "");
165        assert_eq!(message.state, MessageState::Ready);
166        assert_eq!(message.retry_count, 0);
167    }
168
169    #[test]
170    fn test_request_response_structures() {
171        // Test AddMessageRequest
172        let add_req = AddMessageRequest {
173            body: "test message".to_string(),
174        };
175        let json = serde_json::to_string(&add_req).unwrap();
176        assert!(json.contains("\"body\":\"test message\""));
177
178        // Test GetMessagesRequest
179        let get_req = GetMessagesRequest { count: 5 };
180        let json = serde_json::to_string(&get_req).unwrap();
181        assert!(json.contains("\"count\":5"));
182
183        // Test DeleteMessagesRequest
184        use uuid::Uuid;
185        let id1 = Uuid::now_v7();
186        let id2 = Uuid::now_v7();
187        let delete_req = DeleteMessagesRequest {
188            ids: vec![id1, id2],
189        };
190        let json = serde_json::to_string(&delete_req).unwrap();
191        assert!(json.contains("\"ids\":"));
192
193        // Test RetryMessagesRequest
194        let retry_req = RetryMessagesRequest { ids: vec![id1] };
195        let json = serde_json::to_string(&retry_req).unwrap();
196        assert!(json.contains("\"ids\":"));
197    }
198
199    #[test]
200    fn test_response_deserialization() {
201        // Test direct Message response (for add_message)
202        let message_json = r#"{"id":"0198fbd8-344e-7b70-841f-3fbd4b371e4c","body":"test","state":"Ready","lock_until":null,"retry_count":0}"#;
203        let message: Message = serde_json::from_str(message_json).unwrap();
204        assert_eq!(message.body, "test");
205        assert_eq!(message.state, MessageState::Ready);
206        assert_eq!(message.retry_count, 0);
207        assert_eq!(message.lock_until, None);
208
209        // Test array of messages response (for get_messages)
210        let messages_json = r#"[{"id":"0198fbd8-344e-7b70-841f-3fbd4b371e4c","body":"test1","state":"Processing","lock_until":null,"retry_count":1}]"#;
211        let messages: Vec<Message> = serde_json::from_str(messages_json).unwrap();
212        assert_eq!(messages.len(), 1);
213        assert_eq!(messages[0].body, "test1");
214        assert_eq!(messages[0].state, MessageState::Processing);
215
216        // Test success string responses (for delete/retry/purge)
217        let success_response: String = serde_json::from_str(r#""Success""#).unwrap();
218        assert_eq!(success_response, "Success");
219
220        // Test health check response
221        let health_response: String = serde_json::from_str(r#""Hello World""#).unwrap();
222        assert_eq!(health_response, "Hello World");
223    }
224
225    #[test]
226    fn test_malformed_response_deserialization() {
227        // Test that malformed JSON fails gracefully
228        let malformed_json = r#"{"id": invalid}"#;
229        let result = serde_json::from_str::<Message>(malformed_json);
230        assert!(result.is_err());
231
232        // Test missing required fields in Message
233        let incomplete_json = r#"{"id":"0198fbd8-344e-7b70-841f-3fbd4b371e4c","body":"test"}"#; // Missing state and retry_count
234        let result = serde_json::from_str::<Message>(incomplete_json);
235        assert!(result.is_err());
236
237        // Test wrong field types in Message
238        let wrong_type_json = r#"{"id":"0198fbd8-344e-7b70-841f-3fbd4b371e4c","body":"test","state":"Ready","retry_count":"not_a_number"}"#;
239        let result = serde_json::from_str::<Message>(wrong_type_json);
240        assert!(result.is_err());
241
242        // Test malformed message with invalid UUID
243        let bad_uuid_json = r#"{"id":"invalid-uuid","body":"test","state":"Ready","lock_until":null,"retry_count":0}"#;
244        let result = serde_json::from_str::<Message>(bad_uuid_json);
245        assert!(result.is_err()); // Should fail due to invalid UUID
246
247        // Test malformed array
248        let bad_array_json = r#"[{"id":"invalid"}]"#;
249        let result = serde_json::from_str::<Vec<Message>>(bad_array_json);
250        assert!(result.is_err());
251    }
252}