// file: crates/common/game-realtime-transport-lib/unit_tests/contract.rs // version: 1 #[derive(Debug, Eq, PartialEq)] struct TestSender { closed: bool, sent_bytes: usize, } impl crate::RealtimeSender for TestSender { type SendFuture<'a> = std::future::Ready> where Self: 'a; type CloseFuture<'a> = std::future::Ready> where Self: 'a; fn send(&mut self, message: crate::TransportMessage) -> Self::SendFuture<'_> { self.sent_bytes = self.sent_bytes.saturating_add(message.len()); return std::future::ready(Ok(())); } fn close(&mut self) -> Self::CloseFuture<'_> { self.closed = true; return std::future::ready(Ok(())); } } #[derive(Debug, Eq, PartialEq)] struct TestReceiver; impl crate::RealtimeReceiver for TestReceiver { type ReceiveFuture<'a> = std::future::Ready> where Self: 'a; fn receive(&mut self) -> Self::ReceiveFuture<'_> { return std::future::ready(Ok(crate::TransportReceive::Closed)); } } struct TestConnection; impl crate::RealtimeConnection for TestConnection { type Sender = TestSender; type Receiver = TestReceiver; fn split(self) -> (Self::Sender, Self::Receiver) { return (TestSender { closed: false, sent_bytes: 0 }, TestReceiver); } } fn poll_ready(future: impl core::future::Future) -> T { let mut future = std::pin::pin!(future); let waker = std::task::Waker::noop(); let mut context = std::task::Context::from_waker(waker); return match core::future::Future::poll(future.as_mut(), &mut context) { std::task::Poll::Ready(value) => value, std::task::Poll::Pending => unreachable!("test future unexpectedly remained pending"), }; } #[test] fn message_preserves_owned_binary_payload() { let message = crate::TransportMessage::new(vec![0, 1, 2, 255]); assert_eq!(message.as_bytes(), &[0, 1, 2, 255]); assert_eq!(message.len(), 4); assert!(!message.is_empty()); assert_eq!(message.into_bytes(), vec![0, 1, 2, 255]); } #[test] fn empty_message_remains_a_valid_transport_payload() { let message = crate::TransportMessage::new(Vec::new()); assert!(message.is_empty()); assert_eq!(message.len(), 0); } #[test] fn clean_remote_close_is_distinct_from_transport_error() { let mut receiver = TestReceiver; let received = poll_ready(crate::RealtimeReceiver::receive(&mut receiver)); assert_eq!(received, Ok(crate::TransportReceive::Closed)); } #[test] fn connection_split_produces_independent_contract_halves() { let (mut sender, mut receiver) = crate::RealtimeConnection::split(TestConnection); assert_eq!(poll_ready(crate::RealtimeSender::send(&mut sender, crate::TransportMessage::new(vec![1, 2, 3]))), Ok(())); assert_eq!(sender.sent_bytes, 3); assert_eq!(poll_ready(crate::RealtimeReceiver::receive(&mut receiver)), Ok(crate::TransportReceive::Closed)); assert_eq!(poll_ready(crate::RealtimeSender::close(&mut sender)), Ok(())); assert!(sender.closed); } #[test] fn message_receive_variant_preserves_payload() { let received = crate::TransportReceive::Message(crate::TransportMessage::new(vec![4, 5, 6])); assert_eq!(received, crate::TransportReceive::Message(crate::TransportMessage::new(vec![4, 5, 6]))); } #[test] fn transport_error_preserves_stable_kind_and_diagnostic_detail() { let error = crate::TransportError::new(crate::TransportErrorKind::Timeout, "send deadline exceeded"); assert_eq!(error.kind(), crate::TransportErrorKind::Timeout); assert_eq!(error.detail(), "send deadline exceeded"); assert_eq!(error.to_string(), "operation timed out: send deadline exceeded"); } #[test] fn transport_error_kinds_have_stable_human_readable_labels() { let cases = [ (crate::TransportErrorKind::InvalidConfiguration, "invalid configuration"), (crate::TransportErrorKind::Connect, "connect failure"), (crate::TransportErrorKind::Bind, "bind failure"), (crate::TransportErrorKind::Accept, "accept failure"), (crate::TransportErrorKind::Timeout, "operation timed out"), (crate::TransportErrorKind::MessageTooLarge, "message too large"), (crate::TransportErrorKind::Backpressure, "transport backpressure"), (crate::TransportErrorKind::Closed, "connection closed"), (crate::TransportErrorKind::Io, "I/O failure"), (crate::TransportErrorKind::Protocol, "protocol failure"), (crate::TransportErrorKind::Aborted, "operation aborted"), ]; for (kind, expected) in cases { assert_eq!(kind.to_string(), expected); } }