diff --git a/src/network/gossip.rs b/src/network/gossip.rs index fb2498d..fd27e0a 100644 --- a/src/network/gossip.rs +++ b/src/network/gossip.rs @@ -285,3 +285,53 @@ impl RoomState for IrohGossipState { } } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::network::PeerState; + use iroh::SecretKey; + + fn sample_peer_state() -> PeerState { + let secret = SecretKey::generate(); + let public = secret.public(); + let addr = iroh::EndpointAddr::from(public); + PeerState { + name: "TestPeerGossip".to_string(), + is_muted: true, + addr, + } + } + + #[test] + fn test_gossip_message_leave_round_trip() { + let original = GossipMessage::Leave; + let serialized = serde_json::to_string(&original).unwrap(); + let deserialized: GossipMessage = serde_json::from_str(&serialized).unwrap(); + assert!(matches!(deserialized, GossipMessage::Leave)); + } + + #[test] + fn test_gossip_payload_announce_round_trip() { + let peer_state = sample_peer_state(); + let author = peer_state.addr.id; + let payload = GossipPayload { + author, + msg: GossipMessage::Announce(peer_state.clone()), + }; + + let serialized = serde_json::to_string(&payload).unwrap(); + let deserialized: GossipPayload = serde_json::from_str(&serialized).unwrap(); + + assert_eq!(deserialized.author, author); + match deserialized.msg { + GossipMessage::Announce(state) => { + assert_eq!(state, peer_state); + } + GossipMessage::Leave => { + panic!("Expected GossipMessage::Announce, got Leave"); + } + } + } +} + diff --git a/src/network/mod.rs b/src/network/mod.rs index 80ee74a..d73f337 100644 --- a/src/network/mod.rs +++ b/src/network/mod.rs @@ -139,3 +139,63 @@ pub trait RoomState: Send + Sync { pub mod iroh_impl; pub mod gossip; + +#[cfg(test)] +mod tests { + use super::*; + use iroh::{SecretKey, EndpointAddr}; + + fn sample_peer_state() -> PeerState { + let secret = SecretKey::generate(); + let public = secret.public(); + let addr = EndpointAddr::from(public); + PeerState { + name: "TestPeer".to_string(), + is_muted: false, + addr, + } + } + + #[test] + fn test_ticket_round_trip() { + let state = sample_peer_state(); + let topic_id = [7u8; 32]; + let original_ticket = PeerSpeakTicket { + host_addr: state.addr.clone(), + topic_id, + }; + let ticket_str = original_ticket.to_string(); + let parsed_ticket = ticket_str.parse::().unwrap(); + assert_eq!(parsed_ticket.host_addr.id, original_ticket.host_addr.id); + assert_eq!(parsed_ticket.topic_id, original_ticket.topic_id); + } + + #[test] + fn test_malformed_rejection() { + // empty string + let res1 = "".parse::(); + assert!(matches!(res1, Err(NetError::InvalidTicket(_)))); + + // non-base64 garbage + let res2 = "!!!not base64!!!".parse::(); + assert!(matches!(res2, Err(NetError::InvalidTicket(_)))); + + // valid URL-safe-base64 that decodes to non-JSON bytes + let bad_json = b"hello world"; + let encoded = base64::Engine::encode( + &base64::engine::general_purpose::URL_SAFE_NO_PAD, + bad_json, + ); + let res3 = encoded.parse::(); + assert!(matches!(res3, Err(NetError::InvalidTicket(_)))); + } + + #[test] + fn test_peer_state_serde_round_trip() { + let original = sample_peer_state(); + let serialized = serde_json::to_string(&original).unwrap(); + let deserialized: PeerState = serde_json::from_str(&serialized).unwrap(); + assert_eq!(original, deserialized); + } +} +