use rust_xmpp::muc_history::{HistoryRequest, RoomHistory}; fn packet(id: usize, content: &str) -> String { format!( "{content}" ) } fn join(attributes: &str) -> String { format!( "" ) } fn populated() -> RoomHistory { let mut history = RoomHistory::new(3); for id in 1..=4 { history.push( &packet(id, &format!("message {id}")), id as i64 * 10, ); } history } fn ids(packets: &[String]) -> Vec { packets .iter() .map(|xml| { let doc = roxmltree::Document::parse(xml).unwrap(); doc.root_element().attribute("id").unwrap().to_owned() }) .collect() } #[test] fn ordinary_history_is_bounded_and_returns_oldest_first() { let history = populated(); assert_eq!(history.len(), 3); assert_eq!( ids(&history.replay("room@rooms.example", "bob@example/r", "", 40)), ["2", "3", "4"] ); } #[test] fn explicit_count_and_zero_limit() { let history = populated(); assert_eq!( ids(&history.replay("room", "bob@example/r", &join("maxstanzas='2'"), 40)), ["3", "4"] ); for attrs in ["maxstanzas='0'", "maxchars='0'", "maxstanzas='-1'"] { assert!( history .replay("room", "bob@example/r", &join(attrs), 40) .is_empty() ); } } #[test] fn default_count_applies_only_without_valid_explicit_constraints() { let mut history = populated(); history.set_default_messages(1); assert_eq!( ids(&history.replay("room", "bob/r", "", 40)), ["4"] ); assert_eq!( ids(&history.replay("room", "bob/r", &join("seconds='30'"), 40)), ["2", "3", "4"] ); assert_eq!( ids(&history.replay("room", "bob/r", &join("maxstanzas='bad'"), 40)), ["4"] ); } #[test] fn since_and_seconds_use_stricter_cutoff_inclusive() { let history = populated(); let attrs = "since='1970-01-01T00:00:20Z' seconds='10'"; assert_eq!( ids(&history.replay("room", "bob/r", &join(attrs), 40)), ["3", "4"] ); assert_eq!( ids(&history.replay( "room", "bob/r", &join("since='1970-01-01T00:00:30.001Z'"), 40 )), ["4"] ); } #[test] fn replay_carries_room_delay_and_recipient() { let history = populated(); let replay = history.replay("room", "bob@example/r&device", &join("maxstanzas='1'"), 40); let doc = roxmltree::Document::parse(&replay[0]).unwrap(); let message = doc.root_element(); assert_eq!(message.attribute("to"), Some("bob@example/r&device")); assert_eq!(message.attribute("from"), Some("room@rooms.example/Alice")); let delays: Vec<_> = message .children() .filter(|n| n.has_tag_name(("urn:xmpp:delay", "delay"))) .collect(); assert_eq!(delays.len(), 1); assert_eq!(delays[0].attribute("from"), Some("room@rooms.example")); assert_eq!(delays[0].attribute("stamp"), Some("1970-01-01T00:00:40Z")); } #[test] fn maxchars_counts_transmitted_utf8_bytes_including_address_and_delay() { let mut history = RoomHistory::new(3); history.push(&packet(1, "é🦀"), 10); let all = history.replay("room", "bob/r", "", 10); let size = all[0].len(); assert_eq!( history .replay("room", "bob/r", &join(&format!("maxchars='{size}'")), 10) .len(), 1 ); assert!( history .replay( "room", "bob/r", &join(&format!("maxchars='{}'", size - 1)), 10 ) .is_empty() ); assert!( history .replay( "room", "a-much-longer-recipient/r", &join(&format!("maxchars='{size}'")), 10 ) .is_empty() ); } #[test] fn subjects_and_hints_follow_native_history_eligibility() { let mut history = RoomHistory::new(20); for content in [ "topic", "x", "x", "", ] { history.push(&packet(0, content), 1); } assert!(history.is_empty()); for content in [ "", "", "", "", ] { history.push(&packet(1, content), 1); } assert_eq!(history.len(), 4); } #[test] fn capacity_changes_trim_and_zero_disables_history() { let mut history = populated(); history.set_capacity(1); assert_eq!( ids(&history.replay("room", "bob/r", "", 40)), ["4"] ); history.set_capacity(0); history.push(&packet(5, "ignored"), 50); assert!(history.is_empty()); } #[test] fn snapshot_restores_last_message_without_adding_another_delay() { let history = populated(); let (stanza, timestamp) = history.last_message().unwrap(); let mut restored = RoomHistory::new(20); restored.restore_last_message(stanza, timestamp); let replay = restored.replay("room", "bob/r", "", 99); assert_eq!(ids(&replay), ["4"]); let doc = roxmltree::Document::parse(&replay[0]).unwrap(); assert_eq!( doc.root_element() .children() .filter(|n| n.has_tag_name(("urn:xmpp:delay", "delay"))) .count(), 1 ); } #[test] fn only_direct_history_children_are_parsed() { let request = HistoryRequest::parse( "", 40, ); assert!(!request.has_constraints()); let request = HistoryRequest::parse(&join("seconds='NaN' maxchars='inf' maxstanzas='2.9'"), 40); assert_eq!(request.maxstanzas, Some(2)); assert_eq!(request.maxchars, None); assert_eq!(request.since, None); }