1use crate::result::{ProbarError, ProbarResult};
14use serde::{Deserialize, Serialize};
15use std::collections::VecDeque;
16use std::sync::{Arc, Mutex};
17use std::time::Instant;
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
21pub enum WebSocketState {
22 Connecting,
24 Open,
26 Closing,
28 Closed,
30}
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
34pub enum MessageType {
35 Text,
37 Binary,
39 Ping,
41 Pong,
43 Close,
45}
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
49pub enum MessageDirection {
50 Sent,
52 Received,
54}
55
56#[derive(Debug, Clone, Serialize, Deserialize)]
58pub struct WebSocketMessage {
59 pub message_type: MessageType,
61 pub direction: MessageDirection,
63 pub data: String,
65 #[serde(skip)]
67 pub raw_data: Option<Vec<u8>>,
68 pub timestamp_ms: u64,
70 pub connection_id: String,
72}
73
74impl WebSocketMessage {
75 #[must_use]
77 pub fn text(data: &str, direction: MessageDirection, timestamp_ms: u64) -> Self {
78 Self {
79 message_type: MessageType::Text,
80 direction,
81 data: data.to_string(),
82 raw_data: None,
83 timestamp_ms,
84 connection_id: String::new(),
85 }
86 }
87
88 #[must_use]
90 pub fn binary(data: Vec<u8>, direction: MessageDirection, timestamp_ms: u64) -> Self {
91 Self {
92 message_type: MessageType::Binary,
93 direction,
94 data: base64::Engine::encode(&base64::engine::general_purpose::STANDARD, &data),
95 raw_data: Some(data),
96 timestamp_ms,
97 connection_id: String::new(),
98 }
99 }
100
101 #[must_use]
103 pub fn ping(timestamp_ms: u64) -> Self {
104 Self {
105 message_type: MessageType::Ping,
106 direction: MessageDirection::Sent,
107 data: String::new(),
108 raw_data: None,
109 timestamp_ms,
110 connection_id: String::new(),
111 }
112 }
113
114 #[must_use]
116 pub fn pong(timestamp_ms: u64) -> Self {
117 Self {
118 message_type: MessageType::Pong,
119 direction: MessageDirection::Received,
120 data: String::new(),
121 raw_data: None,
122 timestamp_ms,
123 connection_id: String::new(),
124 }
125 }
126
127 #[must_use]
129 pub fn close(code: u16, reason: &str, timestamp_ms: u64) -> Self {
130 Self {
131 message_type: MessageType::Close,
132 direction: MessageDirection::Received,
133 data: format!("{}: {}", code, reason),
134 raw_data: None,
135 timestamp_ms,
136 connection_id: String::new(),
137 }
138 }
139
140 #[must_use]
142 pub fn with_connection(mut self, connection_id: &str) -> Self {
143 self.connection_id = connection_id.to_string();
144 self
145 }
146
147 #[must_use]
149 pub const fn is_text(&self) -> bool {
150 matches!(self.message_type, MessageType::Text)
151 }
152
153 #[must_use]
155 pub const fn is_binary(&self) -> bool {
156 matches!(self.message_type, MessageType::Binary)
157 }
158
159 #[must_use]
161 pub const fn is_sent(&self) -> bool {
162 matches!(self.direction, MessageDirection::Sent)
163 }
164
165 #[must_use]
167 pub const fn is_received(&self) -> bool {
168 matches!(self.direction, MessageDirection::Received)
169 }
170
171 pub fn json<T: for<'de> Deserialize<'de>>(&self) -> ProbarResult<T> {
173 let data = serde_json::from_str(&self.data)?;
174 Ok(data)
175 }
176
177 #[must_use]
179 pub fn contains(&self, s: &str) -> bool {
180 self.data.contains(s)
181 }
182}
183
184#[derive(Debug)]
186pub struct WebSocketConnection {
187 pub id: String,
189 pub url: String,
191 pub state: WebSocketState,
193 messages: Arc<Mutex<Vec<WebSocketMessage>>>,
195 start_time: Instant,
197 pub close_code: Option<u16>,
199 pub close_reason: Option<String>,
201}
202
203impl WebSocketConnection {
204 #[must_use]
206 pub fn new(id: &str, url: &str) -> Self {
207 Self {
208 id: id.to_string(),
209 url: url.to_string(),
210 state: WebSocketState::Connecting,
211 messages: Arc::new(Mutex::new(Vec::new())),
212 start_time: Instant::now(),
213 close_code: None,
214 close_reason: None,
215 }
216 }
217
218 pub fn open(&mut self) {
220 self.state = WebSocketState::Open;
221 }
222
223 pub fn close(&mut self, code: u16, reason: &str) {
225 self.state = WebSocketState::Closed;
226 self.close_code = Some(code);
227 self.close_reason = Some(reason.to_string());
228 }
229
230 #[must_use]
232 pub fn elapsed_ms(&self) -> u64 {
233 self.start_time.elapsed().as_millis() as u64
234 }
235
236 pub fn record_message(&self, mut message: WebSocketMessage) {
238 message.connection_id = self.id.clone();
239 if let Ok(mut messages) = self.messages.lock() {
240 messages.push(message);
241 }
242 }
243
244 pub fn send_text(&self, data: &str) {
246 let message = WebSocketMessage::text(data, MessageDirection::Sent, self.elapsed_ms());
247 self.record_message(message);
248 }
249
250 pub fn send_binary(&self, data: Vec<u8>) {
252 let message = WebSocketMessage::binary(data, MessageDirection::Sent, self.elapsed_ms());
253 self.record_message(message);
254 }
255
256 pub fn receive_text(&self, data: &str) {
258 let message = WebSocketMessage::text(data, MessageDirection::Received, self.elapsed_ms());
259 self.record_message(message);
260 }
261
262 pub fn receive_binary(&self, data: Vec<u8>) {
264 let message = WebSocketMessage::binary(data, MessageDirection::Received, self.elapsed_ms());
265 self.record_message(message);
266 }
267
268 #[must_use]
270 pub fn messages(&self) -> Vec<WebSocketMessage> {
271 self.messages.lock().map(|m| m.clone()).unwrap_or_default()
272 }
273
274 #[must_use]
276 pub fn sent_messages(&self) -> Vec<WebSocketMessage> {
277 self.messages()
278 .into_iter()
279 .filter(|m| m.is_sent())
280 .collect()
281 }
282
283 #[must_use]
285 pub fn received_messages(&self) -> Vec<WebSocketMessage> {
286 self.messages()
287 .into_iter()
288 .filter(|m| m.is_received())
289 .collect()
290 }
291
292 #[must_use]
294 pub fn message_count(&self) -> usize {
295 self.messages.lock().map(|m| m.len()).unwrap_or(0)
296 }
297
298 #[must_use]
300 pub const fn is_open(&self) -> bool {
301 matches!(self.state, WebSocketState::Open)
302 }
303
304 #[must_use]
306 pub const fn is_closed(&self) -> bool {
307 matches!(self.state, WebSocketState::Closed)
308 }
309}
310
311#[derive(Debug, Clone)]
313pub struct MockWebSocketResponse {
314 pub messages: Vec<WebSocketMessage>,
316 pub delay_ms: u64,
318}
319
320impl MockWebSocketResponse {
321 #[must_use]
323 pub fn new() -> Self {
324 Self {
325 messages: Vec::new(),
326 delay_ms: 0,
327 }
328 }
329
330 #[must_use]
332 pub fn with_text(mut self, data: &str) -> Self {
333 self.messages
334 .push(WebSocketMessage::text(data, MessageDirection::Received, 0));
335 self
336 }
337
338 #[must_use]
340 pub fn with_binary(mut self, data: Vec<u8>) -> Self {
341 self.messages.push(WebSocketMessage::binary(
342 data,
343 MessageDirection::Received,
344 0,
345 ));
346 self
347 }
348
349 #[must_use]
351 pub const fn with_delay(mut self, delay_ms: u64) -> Self {
352 self.delay_ms = delay_ms;
353 self
354 }
355}
356
357impl Default for MockWebSocketResponse {
358 fn default() -> Self {
359 Self::new()
360 }
361}
362
363#[derive(Debug, Clone)]
365pub struct WebSocketMock {
366 pub url_pattern: String,
368 pub message_pattern: Option<String>,
370 pub response: MockWebSocketResponse,
372 pub once: bool,
374 pub used: bool,
376}
377
378impl WebSocketMock {
379 #[must_use]
381 pub fn new(url_pattern: &str) -> Self {
382 Self {
383 url_pattern: url_pattern.to_string(),
384 message_pattern: None,
385 response: MockWebSocketResponse::new(),
386 once: false,
387 used: false,
388 }
389 }
390
391 #[must_use]
393 pub fn on_open(mut self, response: MockWebSocketResponse) -> Self {
394 self.response = response;
395 self
396 }
397
398 #[must_use]
400 pub fn on_message(mut self, pattern: &str, response: MockWebSocketResponse) -> Self {
401 self.message_pattern = Some(pattern.to_string());
402 self.response = response;
403 self
404 }
405
406 #[must_use]
408 pub const fn once(mut self) -> Self {
409 self.once = true;
410 self
411 }
412
413 #[must_use]
415 pub fn matches_url(&self, url: &str) -> bool {
416 if self.once && self.used {
417 return false;
418 }
419 url.contains(&self.url_pattern)
420 }
421
422 #[must_use]
424 pub fn matches_message(&self, message: &str) -> bool {
425 if self.once && self.used {
426 return false;
427 }
428 self.message_pattern
429 .as_ref()
430 .is_some_and(|p| message.contains(p))
431 }
432
433 pub fn mark_used(&mut self) {
435 self.used = true;
436 }
437}
438
439#[derive(Debug)]
441pub struct WebSocketMonitor {
442 connections: Arc<Mutex<Vec<WebSocketConnection>>>,
444 mocks: Vec<WebSocketMock>,
446 pending_responses: VecDeque<(String, MockWebSocketResponse)>,
448 active: bool,
450 connection_counter: u64,
452}
453
454impl Default for WebSocketMonitor {
455 fn default() -> Self {
456 Self::new()
457 }
458}
459
460impl WebSocketMonitor {
461 #[must_use]
463 pub fn new() -> Self {
464 Self {
465 connections: Arc::new(Mutex::new(Vec::new())),
466 mocks: Vec::new(),
467 pending_responses: VecDeque::new(),
468 active: false,
469 connection_counter: 0,
470 }
471 }
472
473 pub fn start(&mut self) {
475 self.active = true;
476 }
477
478 pub fn stop(&mut self) {
480 self.active = false;
481 }
482
483 #[must_use]
485 pub const fn is_active(&self) -> bool {
486 self.active
487 }
488
489 pub fn mock(&mut self, mock: WebSocketMock) {
491 self.mocks.push(mock);
492 }
493
494 pub fn connect(&mut self, url: &str) -> String {
496 self.connection_counter += 1;
497 let id = format!("ws_{}", self.connection_counter);
498
499 let mut connection = WebSocketConnection::new(&id, url);
500 connection.open();
501
502 for mock in &mut self.mocks {
504 if mock.matches_url(url) && mock.message_pattern.is_none() {
505 self.pending_responses
506 .push_back((id.clone(), mock.response.clone()));
507 mock.mark_used();
508 }
509 }
510
511 if let Ok(mut connections) = self.connections.lock() {
512 connections.push(connection);
513 }
514
515 id
516 }
517
518 pub fn disconnect(&mut self, connection_id: &str, code: u16, reason: &str) {
520 if let Ok(mut connections) = self.connections.lock() {
521 if let Some(conn) = connections.iter_mut().find(|c| c.id == connection_id) {
522 conn.close(code, reason);
523 }
524 }
525 }
526
527 pub fn send(&mut self, connection_id: &str, message: &str) {
529 if let Ok(connections) = self.connections.lock() {
530 if let Some(conn) = connections.iter().find(|c| c.id == connection_id) {
531 conn.send_text(message);
532
533 for mock in &mut self.mocks {
535 if mock.matches_url(&conn.url) && mock.matches_message(message) {
536 self.pending_responses
537 .push_back((connection_id.to_string(), mock.response.clone()));
538 mock.mark_used();
539 }
540 }
541 }
542 }
543 }
544
545 pub fn receive(&self, connection_id: &str, message: &str) {
547 if let Ok(connections) = self.connections.lock() {
548 if let Some(conn) = connections.iter().find(|c| c.id == connection_id) {
549 conn.receive_text(message);
550 }
551 }
552 }
553
554 #[must_use]
556 pub fn take_pending_responses(&mut self) -> Vec<(String, MockWebSocketResponse)> {
557 self.pending_responses.drain(..).collect()
558 }
559
560 #[must_use]
562 pub fn connections(&self) -> Vec<String> {
563 self.connections
564 .lock()
565 .map(|c| c.iter().map(|conn| conn.id.clone()).collect())
566 .unwrap_or_default()
567 }
568
569 pub fn get_connection(&self, connection_id: &str) -> Option<Vec<WebSocketMessage>> {
571 self.connections.lock().ok().and_then(|connections| {
572 connections
573 .iter()
574 .find(|c| c.id == connection_id)
575 .map(|c| c.messages())
576 })
577 }
578
579 #[must_use]
581 pub fn all_messages(&self) -> Vec<WebSocketMessage> {
582 self.connections
583 .lock()
584 .map(|connections| connections.iter().flat_map(|c| c.messages()).collect())
585 .unwrap_or_default()
586 }
587
588 #[must_use]
590 pub fn connection_count(&self) -> usize {
591 self.connections.lock().map(|c| c.len()).unwrap_or(0)
592 }
593
594 #[must_use]
596 pub fn active_connection_count(&self) -> usize {
597 self.connections
598 .lock()
599 .map(|c| c.iter().filter(|conn| conn.is_open()).count())
600 .unwrap_or(0)
601 }
602
603 pub fn assert_sent(&self, pattern: &str) -> ProbarResult<()> {
605 let messages = self.all_messages();
606 let found = messages.iter().any(|m| m.is_sent() && m.contains(pattern));
607 if !found {
608 return Err(ProbarError::AssertionFailed {
609 message: format!(
610 "Expected sent message containing '{}', but none found",
611 pattern
612 ),
613 });
614 }
615 Ok(())
616 }
617
618 pub fn assert_received(&self, pattern: &str) -> ProbarResult<()> {
620 let messages = self.all_messages();
621 let found = messages
622 .iter()
623 .any(|m| m.is_received() && m.contains(pattern));
624 if !found {
625 return Err(ProbarError::AssertionError {
626 message: format!(
627 "Expected received message containing '{}', but none found",
628 pattern
629 ),
630 });
631 }
632 Ok(())
633 }
634
635 pub fn assert_connected(&self, url_pattern: &str) -> ProbarResult<()> {
637 let found = self
638 .connections
639 .lock()
640 .map(|connections| connections.iter().any(|c| c.url.contains(url_pattern)))
641 .unwrap_or(false);
642
643 if !found {
644 return Err(ProbarError::AssertionError {
645 message: format!(
646 "Expected connection to URL containing '{}', but none found",
647 url_pattern
648 ),
649 });
650 }
651 Ok(())
652 }
653
654 pub fn clear(&mut self) {
656 if let Ok(mut connections) = self.connections.lock() {
657 connections.clear();
658 }
659 self.mocks.clear();
660 self.pending_responses.clear();
661 self.connection_counter = 0;
662 }
663}
664
665#[derive(Debug, Default)]
667pub struct WebSocketMonitorBuilder {
668 monitor: WebSocketMonitor,
669}
670
671impl WebSocketMonitorBuilder {
672 #[must_use]
674 pub fn new() -> Self {
675 Self::default()
676 }
677
678 #[must_use]
680 pub fn mock_open(mut self, url_pattern: &str, response: MockWebSocketResponse) -> Self {
681 self.monitor
682 .mock(WebSocketMock::new(url_pattern).on_open(response));
683 self
684 }
685
686 #[must_use]
688 pub fn mock_message(
689 mut self,
690 url_pattern: &str,
691 message_pattern: &str,
692 response: MockWebSocketResponse,
693 ) -> Self {
694 self.monitor
695 .mock(WebSocketMock::new(url_pattern).on_message(message_pattern, response));
696 self
697 }
698
699 #[must_use]
701 pub fn build(self) -> WebSocketMonitor {
702 self.monitor
703 }
704}
705
706#[cfg(test)]
707#[allow(clippy::unwrap_used, clippy::expect_used)]
708mod tests {
709 use super::*;
710
711 mod websocket_message_tests {
712 use super::*;
713
714 #[test]
715 fn test_text_message() {
716 let msg = WebSocketMessage::text("hello", MessageDirection::Sent, 1000);
717 assert!(msg.is_text());
718 assert!(msg.is_sent());
719 assert_eq!(msg.data, "hello");
720 assert_eq!(msg.timestamp_ms, 1000);
721 }
722
723 #[test]
724 fn test_binary_message() {
725 let msg = WebSocketMessage::binary(vec![1, 2, 3], MessageDirection::Received, 500);
726 assert!(msg.is_binary());
727 assert!(msg.is_received());
728 assert!(msg.raw_data.is_some());
729 }
730
731 #[test]
732 fn test_ping_pong() {
733 let ping = WebSocketMessage::ping(100);
734 assert!(matches!(ping.message_type, MessageType::Ping));
735
736 let pong = WebSocketMessage::pong(200);
737 assert!(matches!(pong.message_type, MessageType::Pong));
738 }
739
740 #[test]
741 fn test_close_message() {
742 let close = WebSocketMessage::close(1000, "Normal closure", 500);
743 assert!(matches!(close.message_type, MessageType::Close));
744 assert!(close.data.contains("1000"));
745 }
746
747 #[test]
748 fn test_with_connection() {
749 let msg =
750 WebSocketMessage::text("test", MessageDirection::Sent, 0).with_connection("conn_1");
751 assert_eq!(msg.connection_id, "conn_1");
752 }
753
754 #[test]
755 fn test_contains() {
756 let msg = WebSocketMessage::text("hello world", MessageDirection::Sent, 0);
757 assert!(msg.contains("world"));
758 assert!(!msg.contains("foo"));
759 }
760
761 #[test]
762 fn test_json() {
763 let msg = WebSocketMessage::text(r#"{"name":"test"}"#, MessageDirection::Sent, 0);
764 let data: serde_json::Value = msg.json().unwrap();
765 assert_eq!(data["name"], "test");
766 }
767 }
768
769 mod websocket_connection_tests {
770 use super::*;
771
772 #[test]
773 fn test_new() {
774 let conn = WebSocketConnection::new("conn_1", "ws://example.com");
775 assert_eq!(conn.id, "conn_1");
776 assert_eq!(conn.url, "ws://example.com");
777 assert!(matches!(conn.state, WebSocketState::Connecting));
778 }
779
780 #[test]
781 fn test_open() {
782 let mut conn = WebSocketConnection::new("conn_1", "ws://example.com");
783 conn.open();
784 assert!(conn.is_open());
785 assert!(!conn.is_closed());
786 }
787
788 #[test]
789 fn test_close() {
790 let mut conn = WebSocketConnection::new("conn_1", "ws://example.com");
791 conn.open();
792 conn.close(1000, "Normal closure");
793
794 assert!(conn.is_closed());
795 assert_eq!(conn.close_code, Some(1000));
796 assert_eq!(conn.close_reason, Some("Normal closure".to_string()));
797 }
798
799 #[test]
800 fn test_send_text() {
801 let conn = WebSocketConnection::new("conn_1", "ws://example.com");
802 conn.send_text("hello");
803
804 let messages = conn.messages();
805 assert_eq!(messages.len(), 1);
806 assert!(messages[0].is_sent());
807 assert_eq!(messages[0].data, "hello");
808 }
809
810 #[test]
811 fn test_receive_text() {
812 let conn = WebSocketConnection::new("conn_1", "ws://example.com");
813 conn.receive_text("response");
814
815 let messages = conn.messages();
816 assert_eq!(messages.len(), 1);
817 assert!(messages[0].is_received());
818 }
819
820 #[test]
821 fn test_sent_received_messages() {
822 let conn = WebSocketConnection::new("conn_1", "ws://example.com");
823 conn.send_text("request");
824 conn.receive_text("response");
825
826 assert_eq!(conn.sent_messages().len(), 1);
827 assert_eq!(conn.received_messages().len(), 1);
828 }
829
830 #[test]
831 fn test_message_count() {
832 let conn = WebSocketConnection::new("conn_1", "ws://example.com");
833 conn.send_text("msg1");
834 conn.send_text("msg2");
835
836 assert_eq!(conn.message_count(), 2);
837 }
838 }
839
840 mod mock_websocket_response_tests {
841 use super::*;
842
843 #[test]
844 fn test_new() {
845 let response = MockWebSocketResponse::new();
846 assert!(response.messages.is_empty());
847 assert_eq!(response.delay_ms, 0);
848 }
849
850 #[test]
851 fn test_with_text() {
852 let response = MockWebSocketResponse::new()
853 .with_text("message 1")
854 .with_text("message 2");
855 assert_eq!(response.messages.len(), 2);
856 }
857
858 #[test]
859 fn test_with_delay() {
860 let response = MockWebSocketResponse::new().with_delay(100);
861 assert_eq!(response.delay_ms, 100);
862 }
863 }
864
865 mod websocket_mock_tests {
866 use super::*;
867
868 #[test]
869 fn test_new() {
870 let mock = WebSocketMock::new("ws://example.com");
871 assert_eq!(mock.url_pattern, "ws://example.com");
872 assert!(mock.message_pattern.is_none());
873 }
874
875 #[test]
876 fn test_matches_url() {
877 let mock = WebSocketMock::new("example.com");
878 assert!(mock.matches_url("ws://example.com/socket"));
879 assert!(!mock.matches_url("ws://other.com"));
880 }
881
882 #[test]
883 fn test_matches_message() {
884 let mock =
885 WebSocketMock::new("example.com").on_message("hello", MockWebSocketResponse::new());
886 assert!(mock.matches_message("say hello world"));
887 assert!(!mock.matches_message("goodbye"));
888 }
889
890 #[test]
891 fn test_once() {
892 let mut mock = WebSocketMock::new("example.com").once();
893 assert!(mock.matches_url("ws://example.com"));
894 mock.mark_used();
895 assert!(!mock.matches_url("ws://example.com"));
896 }
897 }
898
899 mod websocket_monitor_tests {
900 use super::*;
901
902 #[test]
903 fn test_new() {
904 let monitor = WebSocketMonitor::new();
905 assert!(!monitor.is_active());
906 assert_eq!(monitor.connection_count(), 0);
907 }
908
909 #[test]
910 fn test_start_stop() {
911 let mut monitor = WebSocketMonitor::new();
912 monitor.start();
913 assert!(monitor.is_active());
914 monitor.stop();
915 assert!(!monitor.is_active());
916 }
917
918 #[test]
919 fn test_connect() {
920 let mut monitor = WebSocketMonitor::new();
921 let id = monitor.connect("ws://example.com");
922 assert!(!id.is_empty());
923 assert_eq!(monitor.connection_count(), 1);
924 }
925
926 #[test]
927 fn test_disconnect() {
928 let mut monitor = WebSocketMonitor::new();
929 let id = monitor.connect("ws://example.com");
930 monitor.disconnect(&id, 1000, "Normal");
931
932 assert_eq!(monitor.active_connection_count(), 0);
933 }
934
935 #[test]
936 fn test_send() {
937 let mut monitor = WebSocketMonitor::new();
938 let id = monitor.connect("ws://example.com");
939 monitor.send(&id, "hello");
940
941 let messages = monitor.get_connection(&id).unwrap();
942 assert_eq!(messages.len(), 1);
943 assert!(messages[0].is_sent());
944 }
945
946 #[test]
947 fn test_receive() {
948 let mut monitor = WebSocketMonitor::new();
949 let id = monitor.connect("ws://example.com");
950 monitor.receive(&id, "response");
951
952 let messages = monitor.get_connection(&id).unwrap();
953 assert_eq!(messages.len(), 1);
954 assert!(messages[0].is_received());
955 }
956
957 #[test]
958 fn test_all_messages() {
959 let mut monitor = WebSocketMonitor::new();
960 let id1 = monitor.connect("ws://example.com");
961 let id2 = monitor.connect("ws://other.com");
962
963 monitor.send(&id1, "msg1");
964 monitor.send(&id2, "msg2");
965
966 let all = monitor.all_messages();
967 assert_eq!(all.len(), 2);
968 }
969
970 #[test]
971 fn test_mock_on_open() {
972 let mut monitor = WebSocketMonitor::new();
973 monitor.mock(
974 WebSocketMock::new("example.com")
975 .on_open(MockWebSocketResponse::new().with_text("welcome")),
976 );
977
978 let _id = monitor.connect("ws://example.com/socket");
979 let pending = monitor.take_pending_responses();
980
981 assert_eq!(pending.len(), 1);
982 assert_eq!(pending[0].1.messages.len(), 1);
983 }
984
985 #[test]
986 fn test_mock_on_message() {
987 let mut monitor = WebSocketMonitor::new();
988 monitor.mock(
989 WebSocketMock::new("example.com")
990 .on_message("ping", MockWebSocketResponse::new().with_text("pong")),
991 );
992
993 let id = monitor.connect("ws://example.com");
994 monitor.send(&id, "ping");
995
996 let pending = monitor.take_pending_responses();
997 assert_eq!(pending.len(), 1);
998 }
999
1000 #[test]
1001 fn test_assert_sent() {
1002 let mut monitor = WebSocketMonitor::new();
1003 let id = monitor.connect("ws://example.com");
1004 monitor.send(&id, "hello world");
1005
1006 assert!(monitor.assert_sent("hello").is_ok());
1007 assert!(monitor.assert_sent("foo").is_err());
1008 }
1009
1010 #[test]
1011 fn test_assert_received() {
1012 let mut monitor = WebSocketMonitor::new();
1013 let id = monitor.connect("ws://example.com");
1014 monitor.receive(&id, "server response");
1015
1016 assert!(monitor.assert_received("response").is_ok());
1017 assert!(monitor.assert_received("foo").is_err());
1018 }
1019
1020 #[test]
1021 fn test_assert_connected() {
1022 let mut monitor = WebSocketMonitor::new();
1023 let _id = monitor.connect("ws://example.com/socket");
1024
1025 assert!(monitor.assert_connected("example.com").is_ok());
1026 assert!(monitor.assert_connected("other.com").is_err());
1027 }
1028
1029 #[test]
1030 fn test_clear() {
1031 let mut monitor = WebSocketMonitor::new();
1032 let _id = monitor.connect("ws://example.com");
1033 monitor.mock(WebSocketMock::new("test"));
1034
1035 monitor.clear();
1036
1037 assert_eq!(monitor.connection_count(), 0);
1038 }
1039 }
1040
1041 mod websocket_monitor_builder_tests {
1042 use super::*;
1043
1044 #[test]
1045 fn test_builder() {
1046 let monitor = WebSocketMonitorBuilder::new()
1047 .mock_open(
1048 "example.com",
1049 MockWebSocketResponse::new().with_text("hello"),
1050 )
1051 .mock_message(
1052 "example.com",
1053 "ping",
1054 MockWebSocketResponse::new().with_text("pong"),
1055 )
1056 .build();
1057
1058 assert_eq!(monitor.mocks.len(), 2);
1059 }
1060 }
1061
1062 mod additional_coverage_tests {
1063 use super::*;
1064
1065 #[test]
1066 fn test_websocket_connection_send_binary() {
1067 let conn = WebSocketConnection::new("conn_1", "ws://example.com");
1068 conn.send_binary(vec![0x01, 0x02, 0x03, 0x04]);
1069
1070 let messages = conn.messages();
1071 assert_eq!(messages.len(), 1);
1072 assert!(messages[0].is_binary());
1073 assert!(messages[0].is_sent());
1074 assert!(messages[0].raw_data.is_some());
1075 assert_eq!(
1076 messages[0].raw_data.as_ref().unwrap(),
1077 &vec![0x01, 0x02, 0x03, 0x04]
1078 );
1079 }
1080
1081 #[test]
1082 fn test_websocket_connection_receive_binary() {
1083 let conn = WebSocketConnection::new("conn_1", "ws://example.com");
1084 conn.receive_binary(vec![0xDE, 0xAD, 0xBE, 0xEF]);
1085
1086 let messages = conn.messages();
1087 assert_eq!(messages.len(), 1);
1088 assert!(messages[0].is_binary());
1089 assert!(messages[0].is_received());
1090 assert!(messages[0].raw_data.is_some());
1091 }
1092
1093 #[test]
1094 fn test_websocket_connection_elapsed_ms() {
1095 let conn = WebSocketConnection::new("conn_1", "ws://example.com");
1096 let elapsed = conn.elapsed_ms();
1098 assert!(elapsed < 1000); }
1100
1101 #[test]
1102 fn test_mock_websocket_response_with_binary() {
1103 let response = MockWebSocketResponse::new()
1104 .with_binary(vec![1, 2, 3])
1105 .with_binary(vec![4, 5, 6]);
1106 assert_eq!(response.messages.len(), 2);
1107 assert!(response.messages[0].is_binary());
1108 assert!(response.messages[1].is_binary());
1109 }
1110
1111 #[test]
1112 fn test_mock_websocket_response_default() {
1113 let response = MockWebSocketResponse::default();
1114 assert!(response.messages.is_empty());
1115 assert_eq!(response.delay_ms, 0);
1116 }
1117
1118 #[test]
1119 fn test_websocket_monitor_default() {
1120 let monitor = WebSocketMonitor::default();
1121 assert!(!monitor.is_active());
1122 assert_eq!(monitor.connection_count(), 0);
1123 }
1124
1125 #[test]
1126 fn test_websocket_monitor_connections_list() {
1127 let mut monitor = WebSocketMonitor::new();
1128 let id1 = monitor.connect("ws://example1.com");
1129 let id2 = monitor.connect("ws://example2.com");
1130
1131 let connections = monitor.connections();
1132 assert_eq!(connections.len(), 2);
1133 assert!(connections.contains(&id1));
1134 assert!(connections.contains(&id2));
1135 }
1136
1137 #[test]
1138 fn test_websocket_mock_on_open() {
1139 let mock = WebSocketMock::new("example.com")
1140 .on_open(MockWebSocketResponse::new().with_text("welcome"));
1141
1142 assert_eq!(mock.url_pattern, "example.com");
1143 assert!(mock.message_pattern.is_none());
1144 assert_eq!(mock.response.messages.len(), 1);
1145 }
1146
1147 #[test]
1148 fn test_websocket_message_json_error() {
1149 let msg = WebSocketMessage::text("not valid json {{{", MessageDirection::Sent, 0);
1150 let result: Result<serde_json::Value, _> = msg.json();
1151 assert!(result.is_err());
1152 }
1153
1154 #[test]
1155 fn test_websocket_mock_matches_message_no_pattern() {
1156 let mock = WebSocketMock::new("example.com");
1157 assert!(!mock.matches_message("any message"));
1159 }
1160
1161 #[test]
1162 fn test_websocket_mock_once_matches_message() {
1163 let mut mock = WebSocketMock::new("example.com")
1164 .on_message("hello", MockWebSocketResponse::new())
1165 .once();
1166
1167 assert!(mock.matches_message("say hello"));
1168 mock.mark_used();
1169 assert!(!mock.matches_message("say hello"));
1170 }
1171
1172 #[test]
1173 fn test_websocket_monitor_get_connection_not_found() {
1174 let monitor = WebSocketMonitor::new();
1175 let result = monitor.get_connection("nonexistent");
1176 assert!(result.is_none());
1177 }
1178
1179 #[test]
1180 fn test_websocket_monitor_send_to_nonexistent() {
1181 let mut monitor = WebSocketMonitor::new();
1182 monitor.send("nonexistent", "message");
1184 assert_eq!(monitor.all_messages().len(), 0);
1185 }
1186
1187 #[test]
1188 fn test_websocket_monitor_receive_to_nonexistent() {
1189 let monitor = WebSocketMonitor::new();
1190 monitor.receive("nonexistent", "message");
1192 assert_eq!(monitor.all_messages().len(), 0);
1193 }
1194
1195 #[test]
1196 fn test_websocket_monitor_disconnect_nonexistent() {
1197 let mut monitor = WebSocketMonitor::new();
1198 monitor.disconnect("nonexistent", 1000, "Normal");
1200 }
1201
1202 #[test]
1203 fn test_websocket_state_enum_variants() {
1204 let connecting = WebSocketState::Connecting;
1206 let open = WebSocketState::Open;
1207 let closing = WebSocketState::Closing;
1208 let closed = WebSocketState::Closed;
1209
1210 assert!(matches!(connecting, WebSocketState::Connecting));
1211 assert!(matches!(open, WebSocketState::Open));
1212 assert!(matches!(closing, WebSocketState::Closing));
1213 assert!(matches!(closed, WebSocketState::Closed));
1214 }
1215
1216 #[test]
1217 fn test_message_type_enum_variants() {
1218 assert!(matches!(MessageType::Text, MessageType::Text));
1220 assert!(matches!(MessageType::Binary, MessageType::Binary));
1221 assert!(matches!(MessageType::Ping, MessageType::Ping));
1222 assert!(matches!(MessageType::Pong, MessageType::Pong));
1223 assert!(matches!(MessageType::Close, MessageType::Close));
1224 }
1225
1226 #[test]
1227 fn test_message_direction_enum_variants() {
1228 assert!(matches!(MessageDirection::Sent, MessageDirection::Sent));
1230 assert!(matches!(
1231 MessageDirection::Received,
1232 MessageDirection::Received
1233 ));
1234 }
1235
1236 #[test]
1237 fn test_websocket_message_is_not_text() {
1238 let msg = WebSocketMessage::binary(vec![1, 2, 3], MessageDirection::Sent, 0);
1239 assert!(!msg.is_text());
1240 }
1241
1242 #[test]
1243 fn test_websocket_message_is_not_binary() {
1244 let msg = WebSocketMessage::text("hello", MessageDirection::Sent, 0);
1245 assert!(!msg.is_binary());
1246 }
1247
1248 #[test]
1249 fn test_websocket_message_is_not_sent() {
1250 let msg = WebSocketMessage::text("hello", MessageDirection::Received, 0);
1251 assert!(!msg.is_sent());
1252 }
1253
1254 #[test]
1255 fn test_websocket_message_is_not_received() {
1256 let msg = WebSocketMessage::text("hello", MessageDirection::Sent, 0);
1257 assert!(!msg.is_received());
1258 }
1259
1260 #[test]
1261 fn test_websocket_connection_state_transitions() {
1262 let mut conn = WebSocketConnection::new("conn_1", "ws://example.com");
1263 assert!(matches!(conn.state, WebSocketState::Connecting));
1264 assert!(!conn.is_open());
1265 assert!(!conn.is_closed());
1266
1267 conn.open();
1268 assert!(matches!(conn.state, WebSocketState::Open));
1269 assert!(conn.is_open());
1270 assert!(!conn.is_closed());
1271
1272 conn.close(1000, "goodbye");
1273 assert!(matches!(conn.state, WebSocketState::Closed));
1274 assert!(!conn.is_open());
1275 assert!(conn.is_closed());
1276 }
1277
1278 #[test]
1279 fn test_websocket_message_close_format() {
1280 let close = WebSocketMessage::close(1001, "Going Away", 100);
1281 assert!(close.data.contains("1001"));
1282 assert!(close.data.contains("Going Away"));
1283 assert_eq!(close.timestamp_ms, 100);
1284 }
1285
1286 #[test]
1287 fn test_websocket_monitor_multiple_mocks_same_url() {
1288 let mut monitor = WebSocketMonitor::new();
1289 monitor.mock(
1290 WebSocketMock::new("example.com")
1291 .on_open(MockWebSocketResponse::new().with_text("welcome1")),
1292 );
1293 monitor.mock(
1294 WebSocketMock::new("example.com")
1295 .on_open(MockWebSocketResponse::new().with_text("welcome2")),
1296 );
1297
1298 let _id = monitor.connect("ws://example.com/socket");
1299 let pending = monitor.take_pending_responses();
1300
1301 assert_eq!(pending.len(), 2);
1303 }
1304
1305 #[test]
1306 fn test_websocket_monitor_mock_message_url_mismatch() {
1307 let mut monitor = WebSocketMonitor::new();
1308 monitor.mock(
1309 WebSocketMock::new("other.com")
1310 .on_message("hello", MockWebSocketResponse::new().with_text("response")),
1311 );
1312
1313 let id = monitor.connect("ws://example.com");
1314 monitor.send(&id, "hello");
1315
1316 let pending = monitor.take_pending_responses();
1318 assert!(pending.is_empty());
1319 }
1320
1321 #[test]
1322 fn test_websocket_connection_record_message_sets_connection_id() {
1323 let conn = WebSocketConnection::new("my_conn", "ws://example.com");
1324 let msg = WebSocketMessage::text("test", MessageDirection::Sent, 0);
1325
1326 conn.record_message(msg);
1327
1328 let messages = conn.messages();
1329 assert_eq!(messages.len(), 1);
1330 assert_eq!(messages[0].connection_id, "my_conn");
1331 }
1332
1333 #[test]
1334 fn test_websocket_monitor_clear_resets_counter() {
1335 let mut monitor = WebSocketMonitor::new();
1336 let _id1 = monitor.connect("ws://example1.com");
1337 let _id2 = monitor.connect("ws://example2.com");
1338
1339 monitor.clear();
1340
1341 let id3 = monitor.connect("ws://example3.com");
1343 assert_eq!(id3, "ws_1"); }
1345
1346 #[test]
1347 fn test_websocket_monitor_builder_default() {
1348 let builder = WebSocketMonitorBuilder::default();
1349 let monitor = builder.build();
1350 assert!(!monitor.is_active());
1351 assert_eq!(monitor.mocks.len(), 0);
1352 }
1353
1354 #[test]
1355 fn test_websocket_message_ping_direction() {
1356 let ping = WebSocketMessage::ping(50);
1357 assert!(ping.is_sent()); assert_eq!(ping.timestamp_ms, 50);
1359 assert!(ping.data.is_empty());
1360 }
1361
1362 #[test]
1363 fn test_websocket_message_pong_direction() {
1364 let pong = WebSocketMessage::pong(75);
1365 assert!(pong.is_received()); assert_eq!(pong.timestamp_ms, 75);
1367 assert!(pong.data.is_empty());
1368 }
1369
1370 #[test]
1371 fn test_websocket_message_binary_base64_encoding() {
1372 let data = vec![0x48, 0x65, 0x6c, 0x6c, 0x6f]; let msg = WebSocketMessage::binary(data.clone(), MessageDirection::Sent, 0);
1374
1375 assert!(!msg.data.is_empty());
1377 let decoded =
1379 base64::Engine::decode(&base64::engine::general_purpose::STANDARD, &msg.data)
1380 .unwrap();
1381 assert_eq!(decoded, data);
1382 }
1383
1384 #[test]
1385 fn test_websocket_connection_empty_messages() {
1386 let conn = WebSocketConnection::new("conn_1", "ws://example.com");
1387 assert!(conn.messages().is_empty());
1388 assert!(conn.sent_messages().is_empty());
1389 assert!(conn.received_messages().is_empty());
1390 assert_eq!(conn.message_count(), 0);
1391 }
1392
1393 #[test]
1394 fn test_websocket_mock_response_chaining() {
1395 let response = MockWebSocketResponse::new()
1396 .with_text("msg1")
1397 .with_binary(vec![1, 2])
1398 .with_text("msg2")
1399 .with_delay(500);
1400
1401 assert_eq!(response.messages.len(), 3);
1402 assert_eq!(response.delay_ms, 500);
1403 assert!(response.messages[0].is_text());
1404 assert!(response.messages[1].is_binary());
1405 assert!(response.messages[2].is_text());
1406 }
1407
1408 #[test]
1409 fn test_websocket_message_json_valid_nested() {
1410 let msg = WebSocketMessage::text(
1411 r#"{"user":{"name":"Alice","age":30},"active":true}"#,
1412 MessageDirection::Received,
1413 0,
1414 );
1415 let data: serde_json::Value = msg.json().unwrap();
1416 assert_eq!(data["user"]["name"], "Alice");
1417 assert_eq!(data["user"]["age"], 30);
1418 assert_eq!(data["active"], true);
1419 }
1420
1421 #[test]
1422 fn test_websocket_monitor_active_vs_total_count() {
1423 let mut monitor = WebSocketMonitor::new();
1424 let id1 = monitor.connect("ws://example1.com");
1425 let id2 = monitor.connect("ws://example2.com");
1426 let _id3 = monitor.connect("ws://example3.com");
1427
1428 assert_eq!(monitor.connection_count(), 3);
1429 assert_eq!(monitor.active_connection_count(), 3);
1430
1431 monitor.disconnect(&id1, 1000, "Normal");
1432 assert_eq!(monitor.connection_count(), 3);
1433 assert_eq!(monitor.active_connection_count(), 2);
1434
1435 monitor.disconnect(&id2, 1000, "Normal");
1436 assert_eq!(monitor.connection_count(), 3);
1437 assert_eq!(monitor.active_connection_count(), 1);
1438 }
1439
1440 #[test]
1441 fn test_websocket_message_clone() {
1442 let msg = WebSocketMessage::text("hello", MessageDirection::Sent, 100)
1443 .with_connection("conn_1");
1444 let cloned = msg.clone();
1445
1446 assert_eq!(cloned.data, msg.data);
1447 assert_eq!(cloned.timestamp_ms, msg.timestamp_ms);
1448 assert_eq!(cloned.connection_id, msg.connection_id);
1449 }
1450
1451 #[test]
1452 fn test_websocket_mock_clone() {
1453 let mock = WebSocketMock::new("example.com")
1454 .on_message("test", MockWebSocketResponse::new().with_text("response"))
1455 .once();
1456 let cloned = mock.clone();
1457
1458 assert_eq!(cloned.url_pattern, mock.url_pattern);
1459 assert_eq!(cloned.message_pattern, mock.message_pattern);
1460 assert_eq!(cloned.once, mock.once);
1461 }
1462
1463 #[test]
1464 fn test_mock_websocket_response_clone() {
1465 let response = MockWebSocketResponse::new()
1466 .with_text("msg")
1467 .with_delay(100);
1468 let cloned = response.clone();
1469
1470 assert_eq!(cloned.messages.len(), response.messages.len());
1471 assert_eq!(cloned.delay_ms, response.delay_ms);
1472 }
1473
1474 #[test]
1475 fn test_websocket_state_serialization() {
1476 let state = WebSocketState::Open;
1477 let json = serde_json::to_string(&state).unwrap();
1478 let deserialized: WebSocketState = serde_json::from_str(&json).unwrap();
1479 assert_eq!(state, deserialized);
1480 }
1481
1482 #[test]
1483 fn test_message_type_serialization() {
1484 let msg_type = MessageType::Binary;
1485 let json = serde_json::to_string(&msg_type).unwrap();
1486 let deserialized: MessageType = serde_json::from_str(&json).unwrap();
1487 assert_eq!(msg_type, deserialized);
1488 }
1489
1490 #[test]
1491 fn test_message_direction_serialization() {
1492 let direction = MessageDirection::Sent;
1493 let json = serde_json::to_string(&direction).unwrap();
1494 let deserialized: MessageDirection = serde_json::from_str(&json).unwrap();
1495 assert_eq!(direction, deserialized);
1496 }
1497
1498 #[test]
1499 fn test_websocket_message_serialization() {
1500 let msg = WebSocketMessage::text("hello", MessageDirection::Sent, 1000)
1501 .with_connection("conn_1");
1502 let json = serde_json::to_string(&msg).unwrap();
1503 let deserialized: WebSocketMessage = serde_json::from_str(&json).unwrap();
1504
1505 assert_eq!(deserialized.data, "hello");
1506 assert_eq!(deserialized.timestamp_ms, 1000);
1507 assert_eq!(deserialized.connection_id, "conn_1");
1508 assert!(deserialized.raw_data.is_none());
1510 }
1511 }
1512}