From 7bbe4f3af607761ec3fcff0a058eceae7a925f75 Mon Sep 17 00:00:00 2001 From: Mollusk Date: Fri, 5 Jun 2026 21:58:51 -0400 Subject: [PATCH] test: cover chat wire type + history cap GossipMessage::Chat serde round-trips (normal, empty strings, u64::MAX ts, unicode/emoji), GossipPayload{Chat} round-trip, and push_chat history-cap behaviour (single, below cap order-preserved, above cap drops oldest keeping the newest CHAT_HISTORY_MAX in order). Gemini, senior-audited. Co-Authored-By: Claude Opus 4.8 --- src/app/mod.rs | 61 +++++++++++++++++++++++++++++++++ src/network/gossip.rs | 79 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+) diff --git a/src/app/mod.rs b/src/app/mod.rs index 17743d5..e82f121 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -1553,4 +1553,65 @@ mod tests { Some(Sound::Reconnected) ); } + + #[test] + fn test_push_chat_single() { + use super::{push_chat, ChatEntry}; + let mut messages = Vec::new(); + let entry = ChatEntry { + name: "Alice".to_string(), + text: "Hello".to_string(), + mine: true, + }; + push_chat(&mut messages, entry); + assert_eq!(messages.len(), 1); + assert_eq!(messages[0].name, "Alice"); + assert_eq!(messages[0].text, "Hello"); + assert!(messages[0].mine); + } + + #[test] + fn test_push_chat_below_cap() { + use super::{push_chat, ChatEntry, CHAT_HISTORY_MAX}; + let mut messages = Vec::new(); + for i in 0..CHAT_HISTORY_MAX - 10 { + push_chat( + &mut messages, + ChatEntry { + name: format!("User{}", i), + text: format!("Msg{}", i), + mine: i % 2 == 0, + }, + ); + } + assert_eq!(messages.len(), CHAT_HISTORY_MAX - 10); + assert_eq!(messages[0].name, "User0"); + assert_eq!(messages[0].text, "Msg0"); + assert_eq!(messages[messages.len() - 1].name, format!("User{}", CHAT_HISTORY_MAX - 11)); + assert_eq!(messages[messages.len() - 1].text, format!("Msg{}", CHAT_HISTORY_MAX - 11)); + } + + #[test] + fn test_push_chat_above_cap() { + use super::{push_chat, ChatEntry, CHAT_HISTORY_MAX}; + let mut messages = Vec::new(); + let total_pushes = CHAT_HISTORY_MAX + 5; + for i in 0..total_pushes { + push_chat( + &mut messages, + ChatEntry { + name: format!("User{}", i), + text: format!("Msg{}", i), + mine: i % 2 == 0, + }, + ); + } + assert_eq!(messages.len(), CHAT_HISTORY_MAX); + // The first 5 should be dropped. First remaining should be index 5. + assert_eq!(messages[0].name, "User5"); + assert_eq!(messages[0].text, "Msg5"); + // The last remaining should be index total_pushes - 1. + assert_eq!(messages[messages.len() - 1].name, format!("User{}", total_pushes - 1)); + assert_eq!(messages[messages.len() - 1].text, format!("Msg{}", total_pushes - 1)); + } } diff --git a/src/network/gossip.rs b/src/network/gossip.rs index 093e4f1..730525c 100644 --- a/src/network/gossip.rs +++ b/src/network/gossip.rs @@ -372,5 +372,84 @@ mod tests { } } } + + #[test] + fn test_gossip_message_chat_round_trip() { + // Test normal chat message + let original = GossipMessage::Chat { + name: "Alice".to_string(), + text: "Hello".to_string(), + ts: 123456789, + }; + let serialized = serde_json::to_string(&original).unwrap(); + let deserialized: GossipMessage = serde_json::from_str(&serialized).unwrap(); + if let GossipMessage::Chat { name, text, ts } = deserialized { + assert_eq!(name, "Alice"); + assert_eq!(text, "Hello"); + assert_eq!(ts, 123456789); + } else { + panic!("Expected GossipMessage::Chat"); + } + + // Test empty strings and large timestamp + let original_empty = GossipMessage::Chat { + name: "".to_string(), + text: "".to_string(), + ts: u64::MAX, + }; + let serialized_empty = serde_json::to_string(&original_empty).unwrap(); + let deserialized_empty: GossipMessage = serde_json::from_str(&serialized_empty).unwrap(); + if let GossipMessage::Chat { name, text, ts } = deserialized_empty { + assert_eq!(name, ""); + assert_eq!(text, ""); + assert_eq!(ts, u64::MAX); + } else { + panic!("Expected GossipMessage::Chat"); + } + } + + #[test] + fn test_gossip_payload_chat_round_trip() { + let peer_state = sample_peer_state(); + let author = peer_state.addr.id; + let payload = GossipPayload { + author, + msg: GossipMessage::Chat { + name: "Bob".to_string(), + text: "Hi there".to_string(), + ts: 987654321, + }, + }; + + let serialized = serde_json::to_string(&payload).unwrap(); + let deserialized: GossipPayload = serde_json::from_str(&serialized).unwrap(); + + assert_eq!(deserialized.author, author); + if let GossipMessage::Chat { name, text, ts } = deserialized.msg { + assert_eq!(name, "Bob"); + assert_eq!(text, "Hi there"); + assert_eq!(ts, 987654321); + } else { + panic!("Expected GossipMessage::Chat"); + } + } + + #[test] + fn test_gossip_chat_unicode_round_trip() { + let original = GossipMessage::Chat { + name: "🎙 User".to_string(), + text: "héllo 🎙 世界".to_string(), + ts: 1717171717, + }; + let serialized = serde_json::to_string(&original).unwrap(); + let deserialized: GossipMessage = serde_json::from_str(&serialized).unwrap(); + if let GossipMessage::Chat { name, text, ts } = deserialized { + assert_eq!(name, "🎙 User"); + assert_eq!(text, "héllo 🎙 世界"); + assert_eq!(ts, 1717171717); + } else { + panic!("Expected GossipMessage::Chat"); + } + } }