pmcp 2.4.0

High-quality Rust SDK for Model Context Protocol (MCP) with full TypeScript SDK compatibility
Documentation
//! Property-based tests for WebSocket server reliability
//!
//! PMCP-4001: Property tests for connection reliability and message ordering

#![cfg(all(feature = "websocket", not(target_arch = "wasm32")))]

use pmcp::server::transport::websocket_enhanced::*;
use pmcp::shared::TransportMessage;
use proptest::prelude::*;
use std::time::Duration;

#[cfg(test)]
mod websocket_reliability {
    use super::*;

    proptest! {
        /// Property: Server maintains connection count consistency
        #[test]
        fn property_connection_count_consistency(
            max_connections in 1usize..100,
            _actual_connections in 0usize..100
        ) {
            let rt = tokio::runtime::Runtime::new().unwrap();

            rt.block_on(async {
                let config = EnhancedWebSocketConfig {
                    max_connections,
                    ..Default::default()
                };

                let server = EnhancedWebSocketServer::new(config);

                // Initial count should be 0
                prop_assert_eq!(server.client_count().await, 0);

                // Connected clients list should be empty
                prop_assert!(server.get_connected_clients().await.is_empty());

                Ok(())
            })?;
        }

        /// Property: Broadcast configuration is respected
        #[test]
        fn property_broadcast_configuration(
            enable_broadcast in any::<bool>()
        ) {
            let rt = tokio::runtime::Runtime::new().unwrap();

            rt.block_on(async {
                let config = EnhancedWebSocketConfig {
                    enable_broadcast,
                    ..Default::default()
                };

                let server = EnhancedWebSocketServer::new(config);
                let dummy_msg = TransportMessage::Notification(
                    pmcp::types::Notification::Progress(pmcp::types::ProgressNotification::new(
                        pmcp::types::ProgressToken::String("test".to_string()),
                        0.0,
                        None,
                    ))
                );
                let result = server.broadcast(dummy_msg).await;

                if enable_broadcast {
                    // Should succeed (though no clients connected)
                    prop_assert!(result.is_ok());
                } else {
                    // Should fail when disabled
                    prop_assert!(result.is_err());
                }
                Ok(())
            })?;
        }

        /// Property: Heartbeat interval configuration
        #[test]
        fn property_heartbeat_configuration(
            heartbeat_secs in 1u64..300
        ) {
            let config = EnhancedWebSocketConfig {
                heartbeat_interval: Duration::from_secs(heartbeat_secs),
                ..Default::default()
            };

            prop_assert_eq!(config.heartbeat_interval.as_secs(), heartbeat_secs);
        }

        /// Property: Connection timeout configuration
        #[test]
        fn property_connection_timeout(
            timeout_secs in 1u64..600
        ) {
            let config = EnhancedWebSocketConfig {
                connection_timeout: Duration::from_secs(timeout_secs),
                ..Default::default()
            };

            prop_assert_eq!(config.connection_timeout.as_secs(), timeout_secs);
        }

        /// Property: Max connections limit is enforced
        #[test]
        fn property_max_connections_limit(
            max_connections in 1usize..1000,
            bind_port in 10000u16..20000
        ) {
            let config = EnhancedWebSocketConfig {
                max_connections,
                bind_addr: format!("127.0.0.1:{}", bind_port).parse().unwrap(),
                ..Default::default()
            };

            prop_assert_eq!(config.max_connections, max_connections);
            prop_assert!(config.max_connections > 0);
        }

        /// Property: Frame size limits are configurable
        #[test]
        fn property_frame_size_limits(
            max_frame_size in prop::option::of(1024usize..100_000_000),
            max_message_size in prop::option::of(1024usize..100_000_000)
        ) {
            let _config = EnhancedWebSocketConfig {
                max_frame_size,
                max_message_size,
                ..Default::default()
            };

            if let Some(frame_size) = max_frame_size {
                prop_assert!(frame_size >= 1024);
            }

            if let Some(msg_size) = max_message_size {
                prop_assert!(msg_size >= 1024);
            }
        }
    }
}

#[cfg(test)]
mod connection_pooling {
    use super::*;

    proptest! {
        /// Property: Connection pooling configuration
        #[test]
        fn property_connection_pooling(
            enable_pooling in any::<bool>()
        ) {
            let config = EnhancedWebSocketConfig {
                enable_pooling,
                ..Default::default()
            };

            prop_assert_eq!(config.enable_pooling, enable_pooling);
        }

        /// Property: Client ID uniqueness
        #[test]
        fn property_client_id_uniqueness(
            num_ids in 1usize..1000
        ) {
            use uuid::Uuid;

            let mut ids = Vec::new();
            for _ in 0..num_ids {
                ids.push(Uuid::new_v4());
            }

            // All IDs should be unique
            let mut unique_ids = ids.clone();
            unique_ids.sort();
            unique_ids.dedup();

            prop_assert_eq!(ids.len(), unique_ids.len());
        }
    }
}

#[cfg(test)]
mod message_ordering {
    use super::*;

    proptest! {
        /// Property: Message ordering is preserved per client
        #[test]
        fn property_message_ordering(
            messages in prop::collection::vec(any::<u32>(), 1..100)
        ) {
            // In a real implementation, messages from a single client
            // should maintain their order
            let received = messages.clone();

            // Simulate ordered receiving
            let original = messages;

            // Order should be preserved
            for (i, msg) in original.iter().enumerate() {
                if i < received.len() {
                    prop_assert_eq!(*msg, received[i]);
                }
            }
        }
    }
}