Skip to main content

sz_orm_websocket/
lib.rs

1//! # SZ-ORM WebSocket — 实时推送
2//!
3//! 提供 WebSocket 长连接管理、消息推送与认证支持,可选启用 `server` feature
4//! 启动独立 WebSocket 服务。
5//!
6//! ## 主要模块
7//!
8//! - [`handler`] — 连接处理与会话管理
9//! - [`pusher`] — 消息推送器
10//! - [`heartbeat`] — 心跳机制(Ping/Pong)与连接保活
11//! - [`pool`] — 连接池管理(LRU 淘汰、容量限制)
12//! - [`compression`] — 消息压缩(permessage-deflate 模拟)
13//! - [`subprotocol`] — 子协议协商
14//! - [`server`] — WebSocket 服务端(feature = "server")
15
16pub mod compression;
17pub mod error;
18pub mod handler;
19pub mod heartbeat;
20pub mod pool;
21pub mod pusher;
22pub mod subprotocol;
23
24pub use error::WsError;
25pub use handler::*;
26pub use pusher::*;
27
28#[cfg(feature = "server")]
29pub mod server;
30
31#[cfg(feature = "server")]
32pub use server::WsServer;
33
34#[cfg(test)]
35mod tests {
36    use super::*;
37
38    #[test]
39    fn test_websocket_connection_new() {
40        let conn = WebSocketConnection::new("conn1");
41        assert_eq!(conn.id, "conn1");
42        assert!(!conn.is_authenticated);
43        assert!(conn.user_id.is_none());
44    }
45
46    #[test]
47    fn test_websocket_connection_with_user() {
48        let conn = WebSocketConnection::new("conn1").with_user(123);
49        assert_eq!(conn.user_id, Some(123));
50        assert!(conn.is_authenticated);
51    }
52
53    #[test]
54    fn test_websocket_connection_with_address() {
55        let conn = WebSocketConnection::new("conn1").with_address("127.0.0.1:8080");
56        assert_eq!(conn.remote_addr, Some("127.0.0.1:8080".to_string()));
57    }
58
59    #[test]
60    fn test_websocket_connection_subscribe() {
61        let mut conn = WebSocketConnection::new("conn1");
62        conn.subscribe("room1");
63        conn.subscribe("room2");
64        conn.subscribe("room1");
65
66        assert_eq!(conn.subscriptions.len(), 2);
67        assert!(conn.subscriptions.contains(&"room1".to_string()));
68        assert!(conn.subscriptions.contains(&"room2".to_string()));
69    }
70
71    #[test]
72    fn test_websocket_connection_unsubscribe() {
73        let mut conn = WebSocketConnection::new("conn1");
74        conn.subscribe("room1");
75        conn.subscribe("room2");
76        conn.unsubscribe("room1");
77
78        assert_eq!(conn.subscriptions.len(), 1);
79        assert!(!conn.subscriptions.contains(&"room1".to_string()));
80    }
81
82    #[test]
83    fn test_ws_message_builder_text() {
84        let msg = WsMessageBuilder::new().text("hello").build();
85
86        assert_eq!(msg.msg_type, MessageType::Text);
87        assert_eq!(msg.payload, b"hello");
88    }
89
90    #[test]
91    fn test_ws_message_builder_binary() {
92        let msg = WsMessageBuilder::new().binary(vec![1, 2, 3, 4]).build();
93
94        assert_eq!(msg.msg_type, MessageType::Binary);
95        assert_eq!(msg.payload, vec![1, 2, 3, 4]);
96    }
97
98    #[test]
99    fn test_ws_message_builder_json() {
100        let msg = WsMessageBuilder::new()
101            .json(&serde_json::json!({"key": "value"}))
102            .unwrap()
103            .build();
104
105        assert_eq!(msg.msg_type, MessageType::Text);
106        let parsed: serde_json::Value = serde_json::from_slice(&msg.payload).unwrap();
107        assert_eq!(parsed["key"], "value");
108    }
109
110    #[test]
111    fn test_ws_message_builder_with_sender() {
112        let msg = WsMessageBuilder::new()
113            .text("hello")
114            .with_sender(123)
115            .build();
116
117        assert_eq!(msg.sender_id, Some(123));
118    }
119
120    #[test]
121    fn test_ws_message_builder_with_room() {
122        let msg = WsMessageBuilder::new()
123            .text("hello")
124            .with_room("room1")
125            .build();
126
127        assert_eq!(msg.room_id, Some("room1".to_string()));
128    }
129
130    #[test]
131    fn test_ws_message_builder_notification() {
132        let msg = WsMessageBuilder::new()
133            .text("notice")
134            .notification()
135            .build();
136
137        assert_eq!(msg.msg_type, MessageType::Notification);
138    }
139
140    #[test]
141    fn test_ws_message_builder_system() {
142        let msg = WsMessageBuilder::new().text("system").system().build();
143
144        assert_eq!(msg.msg_type, MessageType::System);
145    }
146
147    #[test]
148    fn test_ws_context_new() {
149        let ctx = WsContext::new("conn1");
150        assert_eq!(ctx.connection_id, "conn1");
151        assert!(ctx.user_id.is_none());
152    }
153
154    #[test]
155    fn test_ws_context_with_user() {
156        let ctx = WsContext::new("conn1").with_user(123);
157        assert_eq!(ctx.user_id, Some(123));
158    }
159
160    #[test]
161    fn test_ws_context_with_metadata() {
162        let ctx = WsContext::new("conn1")
163            .with_metadata("key1", "value1")
164            .with_metadata("key2", "value2");
165
166        assert_eq!(ctx.metadata.get("key1"), Some(&"value1".to_string()));
167        assert_eq!(ctx.metadata.get("key2"), Some(&"value2".to_string()));
168    }
169
170    #[test]
171    fn test_push_result_new() {
172        let result = PushResult::new(10);
173        assert_eq!(result.total, 10);
174        assert_eq!(result.success, 0);
175        assert_eq!(result.failed, 0);
176    }
177
178    #[test]
179    fn test_push_result_add() {
180        let mut result = PushResult::new(10);
181        result.add_success();
182        result.add_success();
183        result.add_failure();
184
185        assert_eq!(result.success, 2);
186        assert_eq!(result.failed, 1);
187    }
188
189    #[test]
190    fn test_push_result_success_rate() {
191        let mut result = PushResult::new(10);
192        result.add_success();
193        result.add_success();
194        result.add_success();
195
196        assert_eq!(result.success_rate(), 30.0);
197    }
198
199    #[test]
200    fn test_push_result_zero_total() {
201        let result = PushResult::new(0);
202        assert_eq!(result.success_rate(), 0.0);
203    }
204
205    #[tokio::test]
206    async fn test_realtime_pusher_new() {
207        let pusher = RealtimePusher::new();
208        let count = pusher.connection_count().await;
209        assert_eq!(count, 0);
210    }
211
212    #[tokio::test]
213    async fn test_realtime_pusher_register_connection() {
214        let pusher = RealtimePusher::new();
215        pusher.register_connection("conn1").await;
216
217        let count = pusher.connection_count().await;
218        assert_eq!(count, 1);
219    }
220
221    #[tokio::test]
222    async fn test_realtime_pusher_unregister_connection() {
223        let pusher = RealtimePusher::new();
224        pusher.register_connection("conn1").await;
225        pusher.unregister_connection("conn1").await;
226
227        let count = pusher.connection_count().await;
228        assert_eq!(count, 0);
229    }
230
231    #[tokio::test]
232    async fn test_realtime_pusher_subscribe() {
233        let pusher = RealtimePusher::new();
234        pusher.register_connection("conn1").await;
235        pusher.subscribe("conn1", "room1").await.unwrap();
236
237        let count = pusher.room_count("room1").await;
238        assert_eq!(count, 1);
239    }
240
241    #[tokio::test]
242    async fn test_realtime_pusher_unsubscribe() {
243        let pusher = RealtimePusher::new();
244        pusher.register_connection("conn1").await;
245        pusher.subscribe("conn1", "room1").await.unwrap();
246        pusher.unsubscribe("conn1", "room1").await;
247
248        let count = pusher.room_count("room1").await;
249        assert_eq!(count, 0);
250    }
251
252    #[tokio::test]
253    async fn test_realtime_pusher_room_list() {
254        let pusher = RealtimePusher::new();
255        pusher.register_connection("conn1").await;
256        pusher.subscribe("conn1", "room1").await.unwrap();
257        pusher.subscribe("conn1", "room2").await.unwrap();
258
259        let rooms = pusher.room_list().await;
260        assert!(rooms.contains(&"room1".to_string()));
261        assert!(rooms.contains(&"room2".to_string()));
262    }
263
264    #[tokio::test]
265    async fn test_realtime_pusher_push_to_room() {
266        let pusher = RealtimePusher::new();
267        pusher.register_connection("conn1").await;
268        pusher.subscribe("conn1", "room1").await.unwrap();
269
270        let result = pusher.push_to_room("room1", vec![1, 2, 3]).await;
271        assert!(result.is_ok());
272        // 验证推送成功计数:1 个连接订阅了 room1
273        assert_eq!(result.unwrap(), 1, "应向 1 个连接推送成功");
274    }
275
276    #[tokio::test]
277    async fn test_realtime_pusher_broadcast() {
278        let pusher = RealtimePusher::new();
279        pusher.register_connection("conn1").await;
280        pusher.register_connection("conn2").await;
281
282        let result = pusher.broadcast(vec![1, 2, 3]).await;
283        assert!(result.is_ok());
284        assert_eq!(result.unwrap(), 2);
285    }
286
287    #[tokio::test]
288    async fn test_realtime_pusher_push_order_status() {
289        let pusher = RealtimePusher::new();
290        pusher.register_connection_with_user("conn1", 123).await;
291
292        let result = pusher.push_order_status(123, 456, "shipped").await;
293        assert!(result.is_ok());
294        // 验证推送成功计数:user_id=123 注册了 1 个连接
295        assert_eq!(result.unwrap(), 1, "应向 1 个 user_id=123 的连接推送成功");
296    }
297
298    #[tokio::test]
299    async fn test_realtime_pusher_push_customer_message() {
300        let pusher = RealtimePusher::new();
301        pusher.register_connection("conn1").await;
302        pusher.subscribe("conn1", "room1").await.unwrap();
303
304        let result = pusher.push_customer_message("room1", 123, "hello").await;
305        assert!(result.is_ok());
306        // 验证推送成功计数
307        assert_eq!(result.unwrap(), 1, "应向 1 个订阅了 room1 的连接推送成功");
308    }
309}