1use std::fmt;
13#[cfg(feature = "non-pdk")]
14use std::str::FromStr;
15
16use borsh::{BorshDeserialize, BorshSerialize};
17#[cfg(feature = "non-pdk")]
18use clap::Subcommand;
19use serde::{Deserialize, Serialize};
20
21use crate::rex_info::{RexId, RexUrl, RexValue};
22
23pub const SYSTEM_MESSAGE_SIZE: usize = 128;
26
27#[derive(
31 Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshSerialize, BorshDeserialize,
32)]
33pub enum WebSocketReadMode {
34 #[default]
36 Latest,
37 All,
39 FromIndex(u64),
45}
46
47#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
49pub enum MessageContent {
50 Data(Vec<u8>),
52 OversizedWarning {
54 original_size: usize,
56 limit: usize,
58 original_timestamp: String,
60 },
61}
62
63impl MessageContent {
64 pub fn byte_size(&self) -> usize {
69 match self {
70 MessageContent::Data(data) => data.len(),
71 MessageContent::OversizedWarning { .. } => SYSTEM_MESSAGE_SIZE,
72 }
73 }
74
75 pub fn is_system(&self) -> bool {
77 matches!(self, MessageContent::OversizedWarning { .. })
78 }
79
80 pub fn as_data(&self) -> Option<&[u8]> {
82 match self {
83 MessageContent::Data(data) => Some(data),
84 MessageContent::OversizedWarning { .. } => None,
85 }
86 }
87}
88
89#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
91pub struct BufferedMessage {
92 pub index: u64,
94 pub content: MessageContent,
96 pub received_at: String,
98}
99
100impl BufferedMessage {
101 pub fn byte_size(&self) -> usize {
103 self.content.byte_size()
104 }
105
106 pub fn is_system_message(&self) -> bool {
108 self.content.is_system()
109 }
110
111 pub fn as_data(&self) -> Option<&[u8]> {
113 self.content.as_data()
114 }
115}
116
117#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
140pub struct WebSocketReadResponse {
141 pub messages: Vec<BufferedMessage>,
143 pub latest_index: Option<u64>,
145 pub oldest_index: Option<u64>,
147}
148
149impl WebSocketReadResponse {
150 pub fn has_messages(&self) -> bool {
152 !self.messages.is_empty()
153 }
154
155 pub fn message_count(&self) -> usize {
157 self.messages.len()
158 }
159
160 pub fn first_message(&self) -> Option<&BufferedMessage> {
162 self.messages.first()
163 }
164
165 pub fn latest_message(&self) -> Option<&BufferedMessage> {
167 self.messages.last()
168 }
169
170 pub fn first_data(&self) -> Option<&[u8]> {
174 self.messages.first().and_then(|m| m.as_data())
175 }
176
177 pub fn latest_data(&self) -> Option<&[u8]> {
181 self.messages.last().and_then(|m| m.as_data())
182 }
183
184 pub fn data_messages(&self) -> impl Iterator<Item = &BufferedMessage> {
186 self.messages.iter().filter(|m| !m.is_system_message())
187 }
188
189 pub fn iter_data(&self) -> impl Iterator<Item = &[u8]> {
193 self.messages.iter().filter_map(|m| m.as_data())
194 }
195
196 pub fn data_message_count(&self) -> usize {
198 self.messages
199 .iter()
200 .filter(|m| !m.is_system_message())
201 .count()
202 }
203
204 pub fn has_system_messages(&self) -> bool {
206 self.messages.iter().any(|m| m.is_system_message())
207 }
208}
209
210#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
222#[cfg_attr(feature = "non-pdk", derive(Subcommand))]
223pub enum WebSocketOperation {
224 Connect {
232 #[cfg_attr(feature = "non-pdk", clap(
234 long = "target-url",
235 value_parser = clap::value_parser!(RexUrl)
236 ))]
237 url: RexUrl,
238 #[cfg_attr(feature = "non-pdk", clap(long, value_parser = parse_rex_id))]
240 rex_id: RexId,
241 },
242 Read {
269 #[cfg_attr(feature = "non-pdk", clap(long, value_parser = parse_rex_id))]
271 connection_rex_id: RexId,
272 #[serde(default)]
274 #[cfg_attr(feature = "non-pdk", clap(skip))]
275 mode: WebSocketReadMode,
276 },
277 Send {
301 #[cfg_attr(feature = "non-pdk", clap(long, value_parser = parse_rex_id))]
303 connection_rex_id: RexId,
304 #[cfg_attr(feature = "non-pdk", clap(skip))]
307 messages: Vec<RexValue>,
308 },
309 Close {
330 #[cfg_attr(feature = "non-pdk", clap(long, value_parser = parse_rex_id))]
332 connection_rex_id: RexId,
333 },
334}
335
336#[cfg(feature = "non-pdk")]
338fn parse_rex_id(s: &str) -> Result<RexId, String> {
339 RexId::from_str(s)
340}
341
342impl WebSocketOperation {
343 pub fn connect(url: impl Into<RexUrl>, rex_id: RexId) -> Self {
345 WebSocketOperation::Connect {
346 url: url.into(),
347 rex_id,
348 }
349 }
350
351 pub fn read(connection_rex_id: RexId) -> Self {
353 WebSocketOperation::Read {
354 connection_rex_id,
355 mode: WebSocketReadMode::default(),
356 }
357 }
358
359 pub fn read_with_mode(connection_rex_id: RexId, mode: WebSocketReadMode) -> Self {
361 WebSocketOperation::Read {
362 connection_rex_id,
363 mode,
364 }
365 }
366
367 pub fn send(connection_rex_id: RexId, messages: Vec<RexValue>) -> Self {
369 WebSocketOperation::Send {
370 connection_rex_id,
371 messages,
372 }
373 }
374
375 pub fn close(connection_rex_id: RexId) -> Self {
377 WebSocketOperation::Close { connection_rex_id }
378 }
379
380 pub fn is_connect(&self) -> bool {
382 matches!(self, WebSocketOperation::Connect { .. })
383 }
384
385 pub fn is_read(&self) -> bool {
387 matches!(self, WebSocketOperation::Read { .. })
388 }
389
390 pub fn is_send(&self) -> bool {
392 matches!(self, WebSocketOperation::Send { .. })
393 }
394
395 pub fn is_close(&self) -> bool {
397 matches!(self, WebSocketOperation::Close { .. })
398 }
399
400 pub fn url(&self) -> Option<&RexUrl> {
402 match self {
403 WebSocketOperation::Connect { url, .. } => Some(url),
404 _ => None,
405 }
406 }
407
408 pub fn rex_id(&self) -> Option<&RexId> {
410 match self {
411 WebSocketOperation::Connect { rex_id, .. } => Some(rex_id),
412 _ => None,
413 }
414 }
415
416 pub fn connection_rex_id(&self) -> Option<&RexId> {
418 match self {
419 WebSocketOperation::Read {
420 connection_rex_id, ..
421 }
422 | WebSocketOperation::Send {
423 connection_rex_id, ..
424 }
425 | WebSocketOperation::Close { connection_rex_id } => Some(connection_rex_id),
426 _ => None,
427 }
428 }
429
430 pub fn read_mode(&self) -> Option<&WebSocketReadMode> {
432 match self {
433 WebSocketOperation::Read { mode, .. } => Some(mode),
434 _ => None,
435 }
436 }
437
438 pub fn messages(&self) -> Option<&[RexValue]> {
440 match self {
441 WebSocketOperation::Send { messages, .. } => Some(messages),
442 _ => None,
443 }
444 }
445}
446
447impl fmt::Display for WebSocketOperation {
448 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
449 match self {
450 WebSocketOperation::Connect { url, rex_id } => {
451 write!(f, "Connect(url={url}, rex_id={rex_id})")
452 }
453 WebSocketOperation::Read {
454 connection_rex_id,
455 mode,
456 } => {
457 write!(
458 f,
459 "Read(connection_rex_id={}, mode={:?})",
460 connection_rex_id, mode
461 )
462 }
463 WebSocketOperation::Send {
464 connection_rex_id,
465 messages,
466 } => {
467 write!(
468 f,
469 "Send(connection_rex_id={}, messages_count={})",
470 connection_rex_id,
471 messages.len()
472 )
473 }
474 WebSocketOperation::Close { connection_rex_id } => {
475 write!(f, "Close(connection_rex_id={})", connection_rex_id)
476 }
477 }
478 }
479}
480
481#[cfg(test)]
482mod tests {
483 use rialo_s_pubkey::Pubkey;
484
485 use super::*;
486 use crate::rex_info::{RexInfo, StartingTimestamp, TargetRexProgram, UpdateFrequency};
487
488 fn base_valid_rex_info() -> RexInfo {
489 RexInfo {
490 description: "test".to_string(),
491 target_rex_programs: vec![TargetRexProgram::Time],
492 update_frequency: UpdateFrequency::OneShot,
494 starting_timestamp: StartingTimestamp::Timestamp(0),
496 ..RexInfo::default()
498 }
499 }
500
501 #[test]
506 fn test_websocket_connect_operation_creation() {
507 let rex_id = RexId::new(Pubkey::default(), 1u64);
508 let op = WebSocketOperation::connect("wss://example.com/stream", rex_id);
509 assert!(op.is_connect());
510 assert!(!op.is_read());
511
512 if let WebSocketOperation::Connect {
513 url,
514 rex_id: op_rex_id,
515 } = op
516 {
517 assert_eq!(url.to_string(), "wss://example.com/stream");
518 assert_eq!(op_rex_id, rex_id);
519 } else {
520 panic!("Expected Connect variant");
521 }
522 }
523
524 #[test]
525 fn test_websocket_read_operation_creation() {
526 let rex_id = RexId::new(Pubkey::default(), 42u64);
527 let op = WebSocketOperation::read(rex_id);
528 assert!(op.is_read());
529 assert!(!op.is_connect());
530
531 if let WebSocketOperation::Read {
532 connection_rex_id,
533 mode,
534 } = op
535 {
536 assert_eq!(connection_rex_id, rex_id);
537 assert_eq!(mode, WebSocketReadMode::Latest);
538 } else {
539 panic!("Expected Read variant");
540 }
541 }
542
543 #[test]
544 fn test_websocket_operation_serde_roundtrip_connect() {
545 let rex_id = RexId::new(Pubkey::default(), 1u64);
546 let op = WebSocketOperation::Connect {
547 url: "wss://example.com/stream".into(),
548 rex_id,
549 };
550
551 let json = serde_json::to_string(&op).expect("Failed to serialize");
553 let deserialized: WebSocketOperation =
555 serde_json::from_str(&json).expect("Failed to deserialize");
556
557 assert_eq!(op, deserialized);
558 }
559
560 #[test]
561 fn test_websocket_operation_serde_roundtrip_read() {
562 let rex_id = RexId::new(Pubkey::default(), 123u64);
563 let op = WebSocketOperation::Read {
564 connection_rex_id: rex_id,
565 mode: WebSocketReadMode::default(),
566 };
567
568 let json = serde_json::to_string(&op).expect("Failed to serialize");
570 let deserialized: WebSocketOperation =
572 serde_json::from_str(&json).expect("Failed to deserialize");
573
574 assert_eq!(op, deserialized);
575 }
576
577 #[test]
578 fn test_target_rex_program_websocket_serde_roundtrip() {
579 let rex_id = RexId::new(Pubkey::default(), 1u64);
580 let target = TargetRexProgram::WebSocket(WebSocketOperation::Connect {
581 url: "wss://example.com/stream".into(),
582 rex_id,
583 });
584
585 let json = serde_json::to_string(&target).expect("Failed to serialize");
587 let deserialized: TargetRexProgram =
589 serde_json::from_str(&json).expect("Failed to deserialize");
590
591 assert_eq!(target, deserialized);
592 }
593
594 #[test]
595 fn test_websocket_operation_display() {
596 let rex_id = RexId::new(Pubkey::default(), 1u64);
597 let connect_op = WebSocketOperation::Connect {
598 url: "wss://example.com".into(),
599 rex_id,
600 };
601 let display = format!("{}", connect_op);
602 assert!(display.contains("Connect"));
603 assert!(display.contains("wss://example.com"));
604
605 let rex_id = RexId::new(Pubkey::default(), 1u64);
606 let read_op = WebSocketOperation::Read {
607 connection_rex_id: rex_id,
608 mode: WebSocketReadMode::default(),
609 };
610 let display = format!("{}", read_op);
611 assert!(display.contains("Read"));
612 assert!(display.contains("connection_rex_id"));
613 }
614
615 #[test]
616 fn test_websocket_connect_with_encrypted_url() {
617 let rex_id = RexId::new(Pubkey::default(), 1u64);
618 let op = WebSocketOperation::Connect {
619 url: "enc://encrypted_websocket_url".into(),
620 rex_id,
621 };
622
623 if let WebSocketOperation::Connect { url, .. } = &op {
624 assert_eq!(url.to_string(), "enc://encrypted_websocket_url");
625 }
626
627 let json = serde_json::to_string(&op).expect("Failed to serialize");
629 let deserialized: WebSocketOperation =
630 serde_json::from_str(&json).expect("Failed to deserialize");
631 assert_eq!(op, deserialized);
632 }
633
634 #[test]
635 fn test_rex_info_with_websocket_target() {
636 let rex_id = RexId::new(Pubkey::default(), 1u64);
637 let mut info = base_valid_rex_info();
638 info.target_rex_programs = vec![TargetRexProgram::WebSocket(WebSocketOperation::Connect {
639 url: "wss://example.com/stream".into(),
640 rex_id,
641 })];
642
643 assert!(info.validate().is_ok());
645 }
646
647 #[test]
648 fn test_websocket_operation_borsh_roundtrip_connect() {
649 let rex_id = RexId::new(Pubkey::default(), 1u64);
650 let op = WebSocketOperation::Connect {
651 url: "wss://example.com/stream".into(),
652 rex_id,
653 };
654
655 let bytes = borsh::to_vec(&op).expect("Failed to serialize");
657 let deserialized: WebSocketOperation =
659 borsh::from_slice(&bytes).expect("Failed to deserialize");
660
661 assert_eq!(op, deserialized);
662 }
663
664 #[test]
665 fn test_websocket_operation_borsh_roundtrip_read() {
666 let rex_id = RexId::new(Pubkey::default(), 123u64);
667 let op = WebSocketOperation::Read {
668 connection_rex_id: rex_id,
669 mode: WebSocketReadMode::default(),
670 };
671
672 let bytes = borsh::to_vec(&op).expect("Failed to serialize");
674 let deserialized: WebSocketOperation =
676 borsh::from_slice(&bytes).expect("Failed to deserialize");
677
678 assert_eq!(op, deserialized);
679 }
680
681 #[test]
682 fn test_websocket_operation_borsh_roundtrip_encrypted_url() {
683 let rex_id = RexId::new(Pubkey::default(), 1u64);
684 let op = WebSocketOperation::Connect {
685 url: "enc://encrypted_websocket_url".into(),
686 rex_id,
687 };
688
689 let bytes = borsh::to_vec(&op).expect("Failed to serialize");
691 let deserialized: WebSocketOperation =
693 borsh::from_slice(&bytes).expect("Failed to deserialize");
694
695 assert_eq!(op, deserialized);
696 }
697
698 #[test]
699 fn test_websocket_operation_url_getter() {
700 let rex_id = RexId::new(Pubkey::default(), 1u64);
702 let connect_op = WebSocketOperation::Connect {
703 url: "wss://example.com/stream".into(),
704 rex_id,
705 };
706 assert!(connect_op.url().is_some());
707 assert_eq!(
708 connect_op.url().unwrap().to_string(),
709 "wss://example.com/stream"
710 );
711
712 let rex_id = RexId::new(Pubkey::default(), 42u64);
714 let read_op = WebSocketOperation::Read {
715 connection_rex_id: rex_id,
716 mode: WebSocketReadMode::default(),
717 };
718 assert!(read_op.url().is_none());
719 }
720
721 #[test]
722 fn test_websocket_operation_connection_rex_id_getter() {
723 let rex_id = RexId::new(Pubkey::default(), 42u64);
725 let read_op = WebSocketOperation::Read {
726 connection_rex_id: rex_id,
727 mode: WebSocketReadMode::default(),
728 };
729 assert!(read_op.connection_rex_id().is_some());
730 assert_eq!(*read_op.connection_rex_id().unwrap(), rex_id);
731
732 let rex_id = RexId::new(Pubkey::default(), 1u64);
734 let connect_op = WebSocketOperation::Connect {
735 url: "wss://example.com/stream".into(),
736 rex_id,
737 };
738 assert!(connect_op.connection_rex_id().is_none());
739
740 let rex_id = RexId::new(Pubkey::default(), 99u64);
742 let send_op = WebSocketOperation::Send {
743 connection_rex_id: rex_id,
744 messages: vec![],
745 };
746 assert!(send_op.connection_rex_id().is_some());
747 assert_eq!(*send_op.connection_rex_id().unwrap(), rex_id);
748 }
749
750 #[test]
755 fn test_websocket_send_operation_creation() {
756 let rex_id = RexId::new(Pubkey::default(), 5u64);
757 let messages = vec![
758 RexValue::plain_string("message1"),
759 RexValue::plain_string("message2"),
760 ];
761 let op = WebSocketOperation::send(rex_id, messages.clone());
762 assert!(op.is_send());
763 assert!(!op.is_connect());
764 assert!(!op.is_read());
765
766 if let WebSocketOperation::Send {
767 connection_rex_id,
768 messages: op_messages,
769 } = op
770 {
771 assert_eq!(connection_rex_id, rex_id);
772 assert_eq!(op_messages, messages);
773 } else {
774 panic!("Expected Send variant");
775 }
776 }
777
778 #[test]
779 fn test_websocket_send_messages_getter() {
780 let rex_id = RexId::new(Pubkey::default(), 10u64);
781 let messages = vec![RexValue::plain_string("test")];
782 let send_op = WebSocketOperation::Send {
783 connection_rex_id: rex_id,
784 messages: messages.clone(),
785 };
786
787 assert!(send_op.messages().is_some());
788 assert_eq!(send_op.messages().unwrap(), &messages[..]);
789
790 let connect_op = WebSocketOperation::Connect {
792 url: "wss://example.com".into(),
793 rex_id,
794 };
795 assert!(connect_op.messages().is_none());
796
797 let read_op = WebSocketOperation::Read {
798 connection_rex_id: rex_id,
799 mode: WebSocketReadMode::default(),
800 };
801 assert!(read_op.messages().is_none());
802 }
803
804 #[test]
805 fn test_websocket_operation_serde_roundtrip_send() {
806 let rex_id = RexId::new(Pubkey::default(), 7u64);
807 let messages = vec![
808 RexValue::plain_string("plain_message"),
809 RexValue::encrypted(b"encrypted_data".to_vec()),
810 ];
811 let op = WebSocketOperation::Send {
812 connection_rex_id: rex_id,
813 messages,
814 };
815
816 let json = serde_json::to_string(&op).expect("Failed to serialize");
818 let deserialized: WebSocketOperation =
820 serde_json::from_str(&json).expect("Failed to deserialize");
821
822 assert_eq!(op, deserialized);
823 }
824
825 #[test]
826 fn test_websocket_operation_borsh_roundtrip_send() {
827 let rex_id = RexId::new(Pubkey::default(), 8u64);
828 let messages = vec![
829 RexValue::plain_string("hello"),
830 RexValue::plain_string("world"),
831 ];
832 let op = WebSocketOperation::Send {
833 connection_rex_id: rex_id,
834 messages,
835 };
836
837 let bytes = borsh::to_vec(&op).expect("Failed to serialize");
839 let deserialized: WebSocketOperation =
841 borsh::from_slice(&bytes).expect("Failed to deserialize");
842
843 assert_eq!(op, deserialized);
844 }
845
846 #[test]
847 fn test_websocket_send_display() {
848 let rex_id = RexId::new(Pubkey::default(), 20u64);
849 let messages = vec![
850 RexValue::plain_string("msg1"),
851 RexValue::plain_string("msg2"),
852 RexValue::plain_string("msg3"),
853 ];
854 let send_op = WebSocketOperation::Send {
855 connection_rex_id: rex_id,
856 messages,
857 };
858
859 let display = format!("{}", send_op);
860 assert!(display.contains("Send"));
861 assert!(display.contains("connection_rex_id"));
862 assert!(display.contains("messages_count=3"));
863 }
864
865 #[test]
866 fn test_websocket_send_empty_messages() {
867 let rex_id = RexId::new(Pubkey::default(), 15u64);
868 let op = WebSocketOperation::send(rex_id, vec![]);
869
870 assert!(op.is_send());
871 assert_eq!(op.messages().unwrap().len(), 0);
872
873 let display = format!("{}", op);
874 assert!(display.contains("messages_count=0"));
875 }
876
877 #[test]
878 fn test_websocket_send_with_encrypted_messages() {
879 let rex_id = RexId::new(Pubkey::default(), 25u64);
880 let messages = vec![
881 RexValue::encrypted(b"encrypted1".to_vec()),
882 RexValue::encrypted(b"encrypted2".to_vec()),
883 ];
884 let op = WebSocketOperation::Send {
885 connection_rex_id: rex_id,
886 messages: messages.clone(),
887 };
888
889 let json = serde_json::to_string(&op).expect("Failed to serialize");
891 let deserialized: WebSocketOperation =
892 serde_json::from_str(&json).expect("Failed to deserialize");
893 assert_eq!(op, deserialized);
894
895 let bytes = borsh::to_vec(&op).expect("Failed to serialize");
897 let deserialized: WebSocketOperation =
898 borsh::from_slice(&bytes).expect("Failed to deserialize");
899 assert_eq!(op, deserialized);
900 }
901
902 #[test]
903 fn test_target_rex_program_websocket_send_serde_roundtrip() {
904 let rex_id = RexId::new(Pubkey::default(), 30u64);
905 let messages = vec![RexValue::plain_string("test_message")];
906 let target = TargetRexProgram::WebSocket(WebSocketOperation::Send {
907 connection_rex_id: rex_id,
908 messages,
909 });
910
911 let json = serde_json::to_string(&target).expect("Failed to serialize");
913 let deserialized: TargetRexProgram =
915 serde_json::from_str(&json).expect("Failed to deserialize");
916
917 assert_eq!(target, deserialized);
918 }
919
920 #[test]
925 fn test_websocket_close_operation_creation() {
926 let rex_id = RexId::new(Pubkey::default(), 50u64);
927 let op = WebSocketOperation::close(rex_id);
928 assert!(op.is_close());
929 assert!(!op.is_connect());
930 assert!(!op.is_read());
931 assert!(!op.is_send());
932
933 if let WebSocketOperation::Close { connection_rex_id } = op {
934 assert_eq!(connection_rex_id, rex_id);
935 } else {
936 panic!("Expected Close variant");
937 }
938 }
939
940 #[test]
941 fn test_websocket_close_connection_rex_id_getter() {
942 let rex_id = RexId::new(Pubkey::default(), 55u64);
943 let close_op = WebSocketOperation::Close {
944 connection_rex_id: rex_id,
945 };
946 assert!(close_op.connection_rex_id().is_some());
947 assert_eq!(*close_op.connection_rex_id().unwrap(), rex_id);
948 }
949
950 #[test]
951 fn test_websocket_operation_serde_roundtrip_close() {
952 let rex_id = RexId::new(Pubkey::default(), 60u64);
953 let op = WebSocketOperation::Close {
954 connection_rex_id: rex_id,
955 };
956
957 let json = serde_json::to_string(&op).expect("Failed to serialize");
959 let deserialized: WebSocketOperation =
961 serde_json::from_str(&json).expect("Failed to deserialize");
962
963 assert_eq!(op, deserialized);
964 }
965
966 #[test]
967 fn test_websocket_operation_borsh_roundtrip_close() {
968 let rex_id = RexId::new(Pubkey::default(), 65u64);
969 let op = WebSocketOperation::Close {
970 connection_rex_id: rex_id,
971 };
972
973 let bytes = borsh::to_vec(&op).expect("Failed to serialize");
975 let deserialized: WebSocketOperation =
977 borsh::from_slice(&bytes).expect("Failed to deserialize");
978
979 assert_eq!(op, deserialized);
980 }
981
982 #[test]
983 fn test_websocket_close_display() {
984 let rex_id = RexId::new(Pubkey::default(), 70u64);
985 let close_op = WebSocketOperation::Close {
986 connection_rex_id: rex_id,
987 };
988
989 let display = format!("{}", close_op);
990 assert!(display.contains("Close"));
991 assert!(display.contains("connection_rex_id"));
992 assert!(display.contains(&rex_id.to_string()));
993 }
994
995 #[test]
996 fn test_target_rex_program_websocket_close_serde_roundtrip() {
997 let rex_id = RexId::new(Pubkey::default(), 75u64);
998 let target = TargetRexProgram::WebSocket(WebSocketOperation::Close {
999 connection_rex_id: rex_id,
1000 });
1001
1002 let json = serde_json::to_string(&target).expect("Failed to serialize");
1004 let deserialized: TargetRexProgram =
1006 serde_json::from_str(&json).expect("Failed to deserialize");
1007
1008 assert_eq!(target, deserialized);
1009 }
1010}