diff --git a/crates/ruma-state-res/src/lib.rs b/crates/ruma-state-res/src/lib.rs index b85c35b0..a35fe5f7 100644 --- a/crates/ruma-state-res/src/lib.rs +++ b/crates/ruma-state-res/src/lib.rs @@ -20,8 +20,8 @@ mod error; pub mod event_auth; pub mod room_version; mod state_event; -#[doc(hidden)] -pub mod test_utils; +#[cfg(test)] +mod test_utils; pub use error::{Error, Result}; pub use event_auth::{auth_check, auth_types_for_event}; @@ -628,3 +628,582 @@ pub fn is_power_event(event: &E) -> bool { _ => false, } } + +#[cfg(test)] +mod tests { + use std::{ + collections::{HashMap, HashSet}, + sync::Arc, + }; + + use js_int::uint; + use maplit::{hashmap, hashset}; + use rand::seq::SliceRandom; + use ruma_common::MilliSecondsSinceUnixEpoch; + use ruma_events::{room::join_rules::JoinRule, EventType}; + use ruma_identifiers::{EventId, RoomVersionId}; + use serde_json::json; + use tracing::debug; + + use crate::{ + is_power_event, + room_version::RoomVersion, + test_utils::{ + alice, bob, charlie, do_check, ella, event_id, member_content_ban, member_content_join, + room_id, to_init_pdu_event, to_pdu_event, zara, StateEvent, TestStore, INITIAL_EVENTS, + }, + EventMap, StateMap, + }; + + fn test_event_sort() { + let _ = + tracing::subscriber::set_default(tracing_subscriber::fmt().with_test_writer().finish()); + let events = INITIAL_EVENTS(); + + let event_map = events + .values() + .map(|ev| ((ev.event_type().to_owned(), ev.state_key().to_owned()), ev.clone())) + .collect::>(); + + let auth_chain = HashSet::new(); + + let power_events = event_map + .values() + .filter(|&pdu| is_power_event(&**pdu)) + .map(|pdu| pdu.event_id().clone()) + .collect::>(); + + let sorted_power_events = + crate::reverse_topological_power_sort(power_events, &auth_chain, |id| { + events.get(id).map(Arc::clone) + }) + .unwrap(); + + let resolved_power = crate::iterative_auth_check( + &RoomVersion::version_6(), + &sorted_power_events, + HashMap::new(), // unconflicted events + |id| events.get(id).map(Arc::clone), + ) + .expect("iterative auth check failed on resolved events"); + + // don't remove any events so we know it sorts them all correctly + let mut events_to_sort = events.keys().cloned().collect::>(); + + events_to_sort.shuffle(&mut rand::thread_rng()); + + let power_level = resolved_power.get(&(EventType::RoomPowerLevels, "".to_owned())); + + let sorted_event_ids = + crate::mainline_sort(&events_to_sort, power_level, |id| events.get(id).map(Arc::clone)) + .unwrap(); + + assert_eq!( + vec![ + "$CREATE:foo", + "$IMA:foo", + "$IPOWER:foo", + "$IJR:foo", + "$IMB:foo", + "$IMC:foo", + "$START:foo", + "$END:foo" + ], + sorted_event_ids.iter().map(|id| id.to_string()).collect::>() + ) + } + + #[test] + fn test_sort() { + for _ in 0..20 { + // since we shuffle the eventIds before we sort them introducing randomness + // seems like we should test this a few times + test_event_sort() + } + } + + #[test] + fn ban_vs_power_level() { + let _ = + tracing::subscriber::set_default(tracing_subscriber::fmt().with_test_writer().finish()); + + let events = &[ + to_init_pdu_event( + "PA", + alice(), + EventType::RoomPowerLevels, + Some(""), + json!({ "users": { alice(): 100, bob(): 50 } }), + ), + to_init_pdu_event( + "MA", + alice(), + EventType::RoomMember, + Some(alice().to_string().as_str()), + member_content_join(), + ), + to_init_pdu_event( + "MB", + alice(), + EventType::RoomMember, + Some(bob().to_string().as_str()), + member_content_ban(), + ), + to_init_pdu_event( + "PB", + bob(), + EventType::RoomPowerLevels, + Some(""), + json!({ "users": { alice(): 100, bob(): 50 } }), + ), + ]; + + let edges = vec![vec!["END", "MB", "MA", "PA", "START"], vec!["END", "PA", "PB"]] + .into_iter() + .map(|list| list.into_iter().map(event_id).collect::>()) + .collect::>(); + + let expected_state_ids = + vec!["PA", "MA", "MB"].into_iter().map(event_id).collect::>(); + + do_check(events, edges, expected_state_ids) + } + + #[test] + fn topic_basic() { + let _ = + tracing::subscriber::set_default(tracing_subscriber::fmt().with_test_writer().finish()); + + let events = &[ + to_init_pdu_event("T1", alice(), EventType::RoomTopic, Some(""), json!({})), + to_init_pdu_event( + "PA1", + alice(), + EventType::RoomPowerLevels, + Some(""), + json!({ "users": { alice(): 100, bob(): 50 } }), + ), + to_init_pdu_event("T2", alice(), EventType::RoomTopic, Some(""), json!({})), + to_init_pdu_event( + "PA2", + alice(), + EventType::RoomPowerLevels, + Some(""), + json!({ "users": { alice(): 100, bob(): 0 } }), + ), + to_init_pdu_event( + "PB", + bob(), + EventType::RoomPowerLevels, + Some(""), + json!({ "users": { alice(): 100, bob(): 50 } }), + ), + to_init_pdu_event("T3", bob(), EventType::RoomTopic, Some(""), json!({})), + ]; + + let edges = + vec![vec!["END", "PA2", "T2", "PA1", "T1", "START"], vec!["END", "T3", "PB", "PA1"]] + .into_iter() + .map(|list| list.into_iter().map(event_id).collect::>()) + .collect::>(); + + let expected_state_ids = vec!["PA2", "T2"].into_iter().map(event_id).collect::>(); + + do_check(events, edges, expected_state_ids) + } + + #[test] + fn topic_reset() { + let _ = + tracing::subscriber::set_default(tracing_subscriber::fmt().with_test_writer().finish()); + + let events = &[ + to_init_pdu_event("T1", alice(), EventType::RoomTopic, Some(""), json!({})), + to_init_pdu_event( + "PA", + alice(), + EventType::RoomPowerLevels, + Some(""), + json!({ "users": { alice(): 100, bob(): 50 } }), + ), + to_init_pdu_event("T2", bob(), EventType::RoomTopic, Some(""), json!({})), + to_init_pdu_event( + "MB", + alice(), + EventType::RoomMember, + Some(bob().to_string().as_str()), + member_content_ban(), + ), + ]; + + let edges = vec![vec!["END", "MB", "T2", "PA", "T1", "START"], vec!["END", "T1"]] + .into_iter() + .map(|list| list.into_iter().map(event_id).collect::>()) + .collect::>(); + + let expected_state_ids = + vec!["T1", "MB", "PA"].into_iter().map(event_id).collect::>(); + + do_check(events, edges, expected_state_ids) + } + + #[test] + fn join_rule_evasion() { + let _ = + tracing::subscriber::set_default(tracing_subscriber::fmt().with_test_writer().finish()); + + let events = &[ + to_init_pdu_event( + "JR", + alice(), + EventType::RoomJoinRules, + Some(""), + json!({ "join_rule": JoinRule::Private }), + ), + to_init_pdu_event( + "ME", + ella(), + EventType::RoomMember, + Some(ella().to_string().as_str()), + member_content_join(), + ), + ]; + + let edges = vec![vec!["END", "JR", "START"], vec!["END", "ME", "START"]] + .into_iter() + .map(|list| list.into_iter().map(event_id).collect::>()) + .collect::>(); + + let expected_state_ids = vec![event_id("JR")]; + + do_check(events, edges, expected_state_ids) + } + + #[test] + fn offtopic_power_level() { + let _ = + tracing::subscriber::set_default(tracing_subscriber::fmt().with_test_writer().finish()); + + let events = &[ + to_init_pdu_event( + "PA", + alice(), + EventType::RoomPowerLevels, + Some(""), + json!({ "users": { alice(): 100, bob(): 50 } }), + ), + to_init_pdu_event( + "PB", + bob(), + EventType::RoomPowerLevels, + Some(""), + json!({ "users": { alice(): 100, bob(): 50, charlie(): 50 } }), + ), + to_init_pdu_event( + "PC", + charlie(), + EventType::RoomPowerLevels, + Some(""), + json!({ "users": { alice(): 100, bob(): 50, charlie(): 0 } }), + ), + ]; + + let edges = vec![vec!["END", "PC", "PB", "PA", "START"], vec!["END", "PA"]] + .into_iter() + .map(|list| list.into_iter().map(event_id).collect::>()) + .collect::>(); + + let expected_state_ids = vec!["PC"].into_iter().map(event_id).collect::>(); + + do_check(events, edges, expected_state_ids) + } + + #[test] + fn topic_setting() { + let _ = + tracing::subscriber::set_default(tracing_subscriber::fmt().with_test_writer().finish()); + + let events = &[ + to_init_pdu_event("T1", alice(), EventType::RoomTopic, Some(""), json!({})), + to_init_pdu_event( + "PA1", + alice(), + EventType::RoomPowerLevels, + Some(""), + json!({ "users": { alice(): 100, bob(): 50 } }), + ), + to_init_pdu_event("T2", alice(), EventType::RoomTopic, Some(""), json!({})), + to_init_pdu_event( + "PA2", + alice(), + EventType::RoomPowerLevels, + Some(""), + json!({ "users": { alice(): 100, bob(): 0 } }), + ), + to_init_pdu_event( + "PB", + bob(), + EventType::RoomPowerLevels, + Some(""), + json!({ "users": { alice(): 100, bob(): 50 } }), + ), + to_init_pdu_event("T3", bob(), EventType::RoomTopic, Some(""), json!({})), + to_init_pdu_event("MZ1", zara(), EventType::RoomTopic, Some(""), json!({})), + to_init_pdu_event("T4", alice(), EventType::RoomTopic, Some(""), json!({})), + ]; + + let edges = vec![ + vec!["END", "T4", "MZ1", "PA2", "T2", "PA1", "T1", "START"], + vec!["END", "MZ1", "T3", "PB", "PA1"], + ] + .into_iter() + .map(|list| list.into_iter().map(event_id).collect::>()) + .collect::>(); + + let expected_state_ids = vec!["T4", "PA2"].into_iter().map(event_id).collect::>(); + + do_check(events, edges, expected_state_ids) + } + + #[test] + fn test_event_map_none() { + let _ = + tracing::subscriber::set_default(tracing_subscriber::fmt().with_test_writer().finish()); + + let mut store = TestStore::(hashmap! {}); + + // build up the DAG + let (state_at_bob, state_at_charlie, expected) = store.set_up(); + + let ev_map: EventMap> = store.0.clone(); + let state_sets = vec![state_at_bob, state_at_charlie]; + let resolved = match crate::resolve::( + &RoomVersionId::Version2, + &state_sets, + state_sets + .iter() + .map(|map| { + store + .auth_event_ids(&room_id(), &map.values().cloned().collect::>()) + .unwrap() + }) + .collect(), + |id| ev_map.get(id).map(Arc::clone), + ) { + Ok(state) => state, + Err(e) => panic!("{}", e), + }; + + assert_eq!(expected, resolved) + } + + #[test] + fn test_lexicographical_sort() { + let _ = + tracing::subscriber::set_default(tracing_subscriber::fmt().with_test_writer().finish()); + + let graph = hashmap! { + event_id("l") => hashset![event_id("o")], + event_id("m") => hashset![event_id("n"), event_id("o")], + event_id("n") => hashset![event_id("o")], + event_id("o") => hashset![], // "o" has zero outgoing edges but 4 incoming edges + event_id("p") => hashset![event_id("o")], + }; + + let res = crate::lexicographical_topological_sort(&graph, |id| { + Ok((0, MilliSecondsSinceUnixEpoch(uint!(0)), id.clone())) + }) + .unwrap(); + + assert_eq!( + vec!["o", "l", "n", "m", "p"], + res.iter() + .map(ToString::to_string) + .map(|s| s.replace("$", "").replace(":foo", "")) + .collect::>() + ) + } + + #[test] + fn ban_with_auth_chains() { + let _ = + tracing::subscriber::set_default(tracing_subscriber::fmt().with_test_writer().finish()); + let ban = BAN_STATE_SET(); + + let edges = vec![vec!["END", "MB", "PA", "START"], vec!["END", "IME", "MB"]] + .into_iter() + .map(|list| list.into_iter().map(event_id).collect::>()) + .collect::>(); + + let expected_state_ids = vec!["PA", "MB"].into_iter().map(event_id).collect::>(); + + do_check(&ban.values().cloned().collect::>(), edges, expected_state_ids); + } + + #[test] + fn ban_with_auth_chains2() { + let _ = + tracing::subscriber::set_default(tracing_subscriber::fmt().with_test_writer().finish()); + let init = INITIAL_EVENTS(); + let ban = BAN_STATE_SET(); + + let mut inner = init.clone(); + inner.extend(ban); + let store = TestStore(inner.clone()); + + let state_set_a = [ + inner.get(&event_id("CREATE")).unwrap(), + inner.get(&event_id("IJR")).unwrap(), + inner.get(&event_id("IMA")).unwrap(), + inner.get(&event_id("IMB")).unwrap(), + inner.get(&event_id("IMC")).unwrap(), + inner.get(&event_id("MB")).unwrap(), + inner.get(&event_id("PA")).unwrap(), + ] + .iter() + .map(|ev| ((ev.event_type().to_owned(), ev.state_key().to_owned()), ev.event_id().clone())) + .collect::>(); + + let state_set_b = [ + inner.get(&event_id("CREATE")).unwrap(), + inner.get(&event_id("IJR")).unwrap(), + inner.get(&event_id("IMA")).unwrap(), + inner.get(&event_id("IMB")).unwrap(), + inner.get(&event_id("IMC")).unwrap(), + inner.get(&event_id("IME")).unwrap(), + inner.get(&event_id("PA")).unwrap(), + ] + .iter() + .map(|ev| ((ev.event_type().to_owned(), ev.state_key().to_owned()), ev.event_id().clone())) + .collect::>(); + + let ev_map: EventMap> = store.0.clone(); + let state_sets = vec![state_set_a, state_set_b]; + let resolved = match crate::resolve::( + &RoomVersionId::Version6, + &state_sets, + state_sets + .iter() + .map(|map| { + store + .auth_event_ids(&room_id(), &map.values().cloned().collect::>()) + .unwrap() + }) + .collect(), + |id| ev_map.get(id).map(Arc::clone), + ) { + Ok(state) => state, + Err(e) => panic!("{}", e), + }; + + debug!( + "{:#?}", + resolved + .iter() + .map(|((ty, key), id)| format!("(({}{:?}), {})", ty, key, id)) + .collect::>() + ); + + let expected = vec![ + "$CREATE:foo", + "$IJR:foo", + "$PA:foo", + "$IMA:foo", + "$IMB:foo", + "$IMC:foo", + "$MB:foo", + ]; + + for id in expected.iter().map(|i| event_id(i)) { + // make sure our resolved events are equal to the expected list + assert!(resolved.values().any(|eid| eid == &id) || init.contains_key(&id), "{}", id) + } + assert_eq!(expected.len(), resolved.len()) + } + + #[test] + fn join_rule_with_auth_chain() { + let join_rule = JOIN_RULE(); + + let edges = vec![vec!["END", "JR", "START"], vec!["END", "IMZ", "START"]] + .into_iter() + .map(|list| list.into_iter().map(event_id).collect::>()) + .collect::>(); + + let expected_state_ids = vec!["JR"].into_iter().map(event_id).collect::>(); + + do_check(&join_rule.values().cloned().collect::>(), edges, expected_state_ids); + } + + #[allow(non_snake_case)] + fn BAN_STATE_SET() -> HashMap> { + vec![ + to_pdu_event( + "PA", + alice(), + EventType::RoomPowerLevels, + Some(""), + json!({ "users": { alice(): 100, bob(): 50 } }), + &["CREATE", "IMA", "IPOWER"], // auth_events + &["START"], // prev_events + ), + to_pdu_event( + "PB", + alice(), + EventType::RoomPowerLevels, + Some(""), + json!({ "users": { alice(): 100, bob(): 50 } }), + &["CREATE", "IMA", "IPOWER"], + &["END"], + ), + to_pdu_event( + "MB", + alice(), + EventType::RoomMember, + Some(ella().as_str()), + member_content_ban(), + &["CREATE", "IMA", "PB"], + &["PA"], + ), + to_pdu_event( + "IME", + ella(), + EventType::RoomMember, + Some(ella().as_str()), + member_content_join(), + &["CREATE", "IJR", "PA"], + &["MB"], + ), + ] + .into_iter() + .map(|ev| (ev.event_id().clone(), ev)) + .collect() + } + + #[allow(non_snake_case)] + fn JOIN_RULE() -> HashMap> { + vec![ + to_pdu_event( + "JR", + alice(), + EventType::RoomJoinRules, + Some(""), + json!({ "join_rule": "invite" }), + &["CREATE", "IMA", "IPOWER"], + &["START"], + ), + to_pdu_event( + "IMZ", + zara(), + EventType::RoomPowerLevels, + Some(zara().as_str()), + member_content_join(), + &["CREATE", "JR", "IPOWER"], + &["START"], + ), + ] + .into_iter() + .map(|ev| (ev.event_id().clone(), ev)) + .collect() + } +} diff --git a/crates/ruma-state-res/tests/event_sorting.rs b/crates/ruma-state-res/tests/event_sorting.rs deleted file mode 100644 index f2497785..00000000 --- a/crates/ruma-state-res/tests/event_sorting.rs +++ /dev/null @@ -1,77 +0,0 @@ -use std::{ - collections::{HashMap, HashSet}, - sync::Arc, -}; - -use rand::seq::SliceRandom; -use ruma_events::EventType; -use ruma_state_res::{ - self as state_res, is_power_event, room_version::RoomVersion, test_utils::INITIAL_EVENTS, - StateMap, -}; - -fn test_event_sort() { - let _ = tracing::subscriber::set_default(tracing_subscriber::fmt().with_test_writer().finish()); - let events = INITIAL_EVENTS(); - - let event_map = events - .values() - .map(|ev| ((ev.event_type().to_owned(), ev.state_key().to_owned()), ev.clone())) - .collect::>(); - - let auth_chain = HashSet::new(); - - let power_events = event_map - .values() - .filter(|&pdu| is_power_event(&**pdu)) - .map(|pdu| pdu.event_id().clone()) - .collect::>(); - - let sorted_power_events = - state_res::reverse_topological_power_sort(power_events, &auth_chain, |id| { - events.get(id).map(Arc::clone) - }) - .unwrap(); - - let resolved_power = state_res::iterative_auth_check( - &RoomVersion::version_6(), - &sorted_power_events, - HashMap::new(), // unconflicted events - |id| events.get(id).map(Arc::clone), - ) - .expect("iterative auth check failed on resolved events"); - - // don't remove any events so we know it sorts them all correctly - let mut events_to_sort = events.keys().cloned().collect::>(); - - events_to_sort.shuffle(&mut rand::thread_rng()); - - let power_level = resolved_power.get(&(EventType::RoomPowerLevels, "".to_owned())); - - let sorted_event_ids = - state_res::mainline_sort(&events_to_sort, power_level, |id| events.get(id).map(Arc::clone)) - .unwrap(); - - assert_eq!( - vec![ - "$CREATE:foo", - "$IMA:foo", - "$IPOWER:foo", - "$IJR:foo", - "$IMB:foo", - "$IMC:foo", - "$START:foo", - "$END:foo" - ], - sorted_event_ids.iter().map(|id| id.to_string()).collect::>() - ) -} - -#[test] -fn test_sort() { - for _ in 0..20 { - // since we shuffle the eventIds before we sort them introducing randomness - // seems like we should test this a few times - test_event_sort() - } -} diff --git a/crates/ruma-state-res/tests/res_with_auth_ids.rs b/crates/ruma-state-res/tests/res_with_auth_ids.rs deleted file mode 100644 index ab96fbf5..00000000 --- a/crates/ruma-state-res/tests/res_with_auth_ids.rs +++ /dev/null @@ -1,190 +0,0 @@ -#![allow(clippy::or_fun_call, clippy::expect_fun_call)] - -use std::{collections::HashMap, sync::Arc}; - -use ruma_events::EventType; -use ruma_identifiers::{EventId, RoomVersionId}; -use ruma_state_res::{ - self as state_res, - test_utils::{ - alice, bob, do_check, ella, event_id, member_content_ban, member_content_join, room_id, - to_pdu_event, zara, StateEvent, TestStore, INITIAL_EVENTS, - }, - EventMap, StateMap, -}; -use serde_json::json; -use tracing::debug; - -#[test] -fn ban_with_auth_chains() { - let _ = tracing::subscriber::set_default(tracing_subscriber::fmt().with_test_writer().finish()); - let ban = BAN_STATE_SET(); - - let edges = vec![vec!["END", "MB", "PA", "START"], vec!["END", "IME", "MB"]] - .into_iter() - .map(|list| list.into_iter().map(event_id).collect::>()) - .collect::>(); - - let expected_state_ids = vec!["PA", "MB"].into_iter().map(event_id).collect::>(); - - do_check(&ban.values().cloned().collect::>(), edges, expected_state_ids); -} - -#[test] -fn ban_with_auth_chains2() { - let _ = tracing::subscriber::set_default(tracing_subscriber::fmt().with_test_writer().finish()); - let init = INITIAL_EVENTS(); - let ban = BAN_STATE_SET(); - - let mut inner = init.clone(); - inner.extend(ban); - let store = TestStore(inner.clone()); - - let state_set_a = [ - inner.get(&event_id("CREATE")).unwrap(), - inner.get(&event_id("IJR")).unwrap(), - inner.get(&event_id("IMA")).unwrap(), - inner.get(&event_id("IMB")).unwrap(), - inner.get(&event_id("IMC")).unwrap(), - inner.get(&event_id("MB")).unwrap(), - inner.get(&event_id("PA")).unwrap(), - ] - .iter() - .map(|ev| ((ev.event_type().to_owned(), ev.state_key().to_owned()), ev.event_id().clone())) - .collect::>(); - - let state_set_b = [ - inner.get(&event_id("CREATE")).unwrap(), - inner.get(&event_id("IJR")).unwrap(), - inner.get(&event_id("IMA")).unwrap(), - inner.get(&event_id("IMB")).unwrap(), - inner.get(&event_id("IMC")).unwrap(), - inner.get(&event_id("IME")).unwrap(), - inner.get(&event_id("PA")).unwrap(), - ] - .iter() - .map(|ev| ((ev.event_type().to_owned(), ev.state_key().to_owned()), ev.event_id().clone())) - .collect::>(); - - let ev_map: EventMap> = store.0.clone(); - let state_sets = vec![state_set_a, state_set_b]; - let resolved = match state_res::resolve::( - &RoomVersionId::Version6, - &state_sets, - state_sets - .iter() - .map(|map| { - store - .auth_event_ids(&room_id(), &map.values().cloned().collect::>()) - .unwrap() - }) - .collect(), - |id| ev_map.get(id).map(Arc::clone), - ) { - Ok(state) => state, - Err(e) => panic!("{}", e), - }; - - debug!( - "{:#?}", - resolved - .iter() - .map(|((ty, key), id)| format!("(({}{:?}), {})", ty, key, id)) - .collect::>() - ); - - let expected = - vec!["$CREATE:foo", "$IJR:foo", "$PA:foo", "$IMA:foo", "$IMB:foo", "$IMC:foo", "$MB:foo"]; - - for id in expected.iter().map(|i| event_id(i)) { - // make sure our resolved events are equal to the expected list - assert!(resolved.values().any(|eid| eid == &id) || init.contains_key(&id), "{}", id) - } - assert_eq!(expected.len(), resolved.len()) -} - -#[test] -fn join_rule_with_auth_chain() { - let join_rule = JOIN_RULE(); - - let edges = vec![vec!["END", "JR", "START"], vec!["END", "IMZ", "START"]] - .into_iter() - .map(|list| list.into_iter().map(event_id).collect::>()) - .collect::>(); - - let expected_state_ids = vec!["JR"].into_iter().map(event_id).collect::>(); - - do_check(&join_rule.values().cloned().collect::>(), edges, expected_state_ids); -} - -#[allow(non_snake_case)] -fn BAN_STATE_SET() -> HashMap> { - vec![ - to_pdu_event( - "PA", - alice(), - EventType::RoomPowerLevels, - Some(""), - json!({ "users": { alice(): 100, bob(): 50 } }), - &["CREATE", "IMA", "IPOWER"], // auth_events - &["START"], // prev_events - ), - to_pdu_event( - "PB", - alice(), - EventType::RoomPowerLevels, - Some(""), - json!({ "users": { alice(): 100, bob(): 50 } }), - &["CREATE", "IMA", "IPOWER"], - &["END"], - ), - to_pdu_event( - "MB", - alice(), - EventType::RoomMember, - Some(ella().as_str()), - member_content_ban(), - &["CREATE", "IMA", "PB"], - &["PA"], - ), - to_pdu_event( - "IME", - ella(), - EventType::RoomMember, - Some(ella().as_str()), - member_content_join(), - &["CREATE", "IJR", "PA"], - &["MB"], - ), - ] - .into_iter() - .map(|ev| (ev.event_id().clone(), ev)) - .collect() -} - -#[allow(non_snake_case)] -fn JOIN_RULE() -> HashMap> { - vec![ - to_pdu_event( - "JR", - alice(), - EventType::RoomJoinRules, - Some(""), - json!({ "join_rule": "invite" }), - &["CREATE", "IMA", "IPOWER"], - &["START"], - ), - to_pdu_event( - "IMZ", - zara(), - EventType::RoomPowerLevels, - Some(zara().as_str()), - member_content_join(), - &["CREATE", "JR", "IPOWER"], - &["START"], - ), - ] - .into_iter() - .map(|ev| (ev.event_id().clone(), ev)) - .collect() -} diff --git a/crates/ruma-state-res/tests/state_res.rs b/crates/ruma-state-res/tests/state_res.rs deleted file mode 100644 index 634e0032..00000000 --- a/crates/ruma-state-res/tests/state_res.rs +++ /dev/null @@ -1,308 +0,0 @@ -use std::sync::Arc; - -use js_int::uint; -use maplit::{hashmap, hashset}; -use ruma_common::MilliSecondsSinceUnixEpoch; -use ruma_events::{room::join_rules::JoinRule, EventType}; -use ruma_identifiers::RoomVersionId; -use ruma_state_res::{ - self as state_res, - test_utils::{ - alice, bob, charlie, do_check, ella, event_id, member_content_ban, member_content_join, - room_id, to_init_pdu_event, zara, StateEvent, TestStore, - }, - EventMap, -}; -use serde_json::json; - -#[test] -fn ban_vs_power_level() { - let _ = tracing::subscriber::set_default(tracing_subscriber::fmt().with_test_writer().finish()); - - let events = &[ - to_init_pdu_event( - "PA", - alice(), - EventType::RoomPowerLevels, - Some(""), - json!({ "users": { alice(): 100, bob(): 50 } }), - ), - to_init_pdu_event( - "MA", - alice(), - EventType::RoomMember, - Some(alice().to_string().as_str()), - member_content_join(), - ), - to_init_pdu_event( - "MB", - alice(), - EventType::RoomMember, - Some(bob().to_string().as_str()), - member_content_ban(), - ), - to_init_pdu_event( - "PB", - bob(), - EventType::RoomPowerLevels, - Some(""), - json!({ "users": { alice(): 100, bob(): 50 } }), - ), - ]; - - let edges = vec![vec!["END", "MB", "MA", "PA", "START"], vec!["END", "PA", "PB"]] - .into_iter() - .map(|list| list.into_iter().map(event_id).collect::>()) - .collect::>(); - - let expected_state_ids = vec!["PA", "MA", "MB"].into_iter().map(event_id).collect::>(); - - do_check(events, edges, expected_state_ids) -} - -#[test] -fn topic_basic() { - let _ = tracing::subscriber::set_default(tracing_subscriber::fmt().with_test_writer().finish()); - - let events = &[ - to_init_pdu_event("T1", alice(), EventType::RoomTopic, Some(""), json!({})), - to_init_pdu_event( - "PA1", - alice(), - EventType::RoomPowerLevels, - Some(""), - json!({ "users": { alice(): 100, bob(): 50 } }), - ), - to_init_pdu_event("T2", alice(), EventType::RoomTopic, Some(""), json!({})), - to_init_pdu_event( - "PA2", - alice(), - EventType::RoomPowerLevels, - Some(""), - json!({ "users": { alice(): 100, bob(): 0 } }), - ), - to_init_pdu_event( - "PB", - bob(), - EventType::RoomPowerLevels, - Some(""), - json!({ "users": { alice(): 100, bob(): 50 } }), - ), - to_init_pdu_event("T3", bob(), EventType::RoomTopic, Some(""), json!({})), - ]; - - let edges = - vec![vec!["END", "PA2", "T2", "PA1", "T1", "START"], vec!["END", "T3", "PB", "PA1"]] - .into_iter() - .map(|list| list.into_iter().map(event_id).collect::>()) - .collect::>(); - - let expected_state_ids = vec!["PA2", "T2"].into_iter().map(event_id).collect::>(); - - do_check(events, edges, expected_state_ids) -} - -#[test] -fn topic_reset() { - let _ = tracing::subscriber::set_default(tracing_subscriber::fmt().with_test_writer().finish()); - - let events = &[ - to_init_pdu_event("T1", alice(), EventType::RoomTopic, Some(""), json!({})), - to_init_pdu_event( - "PA", - alice(), - EventType::RoomPowerLevels, - Some(""), - json!({ "users": { alice(): 100, bob(): 50 } }), - ), - to_init_pdu_event("T2", bob(), EventType::RoomTopic, Some(""), json!({})), - to_init_pdu_event( - "MB", - alice(), - EventType::RoomMember, - Some(bob().to_string().as_str()), - member_content_ban(), - ), - ]; - - let edges = vec![vec!["END", "MB", "T2", "PA", "T1", "START"], vec!["END", "T1"]] - .into_iter() - .map(|list| list.into_iter().map(event_id).collect::>()) - .collect::>(); - - let expected_state_ids = vec!["T1", "MB", "PA"].into_iter().map(event_id).collect::>(); - - do_check(events, edges, expected_state_ids) -} - -#[test] -fn join_rule_evasion() { - let _ = tracing::subscriber::set_default(tracing_subscriber::fmt().with_test_writer().finish()); - - let events = &[ - to_init_pdu_event( - "JR", - alice(), - EventType::RoomJoinRules, - Some(""), - json!({ "join_rule": JoinRule::Private }), - ), - to_init_pdu_event( - "ME", - ella(), - EventType::RoomMember, - Some(ella().to_string().as_str()), - member_content_join(), - ), - ]; - - let edges = vec![vec!["END", "JR", "START"], vec!["END", "ME", "START"]] - .into_iter() - .map(|list| list.into_iter().map(event_id).collect::>()) - .collect::>(); - - let expected_state_ids = vec![event_id("JR")]; - - do_check(events, edges, expected_state_ids) -} - -#[test] -fn offtopic_power_level() { - let _ = tracing::subscriber::set_default(tracing_subscriber::fmt().with_test_writer().finish()); - - let events = &[ - to_init_pdu_event( - "PA", - alice(), - EventType::RoomPowerLevels, - Some(""), - json!({ "users": { alice(): 100, bob(): 50 } }), - ), - to_init_pdu_event( - "PB", - bob(), - EventType::RoomPowerLevels, - Some(""), - json!({ "users": { alice(): 100, bob(): 50, charlie(): 50 } }), - ), - to_init_pdu_event( - "PC", - charlie(), - EventType::RoomPowerLevels, - Some(""), - json!({ "users": { alice(): 100, bob(): 50, charlie(): 0 } }), - ), - ]; - - let edges = vec![vec!["END", "PC", "PB", "PA", "START"], vec!["END", "PA"]] - .into_iter() - .map(|list| list.into_iter().map(event_id).collect::>()) - .collect::>(); - - let expected_state_ids = vec!["PC"].into_iter().map(event_id).collect::>(); - - do_check(events, edges, expected_state_ids) -} - -#[test] -fn topic_setting() { - let _ = tracing::subscriber::set_default(tracing_subscriber::fmt().with_test_writer().finish()); - - let events = &[ - to_init_pdu_event("T1", alice(), EventType::RoomTopic, Some(""), json!({})), - to_init_pdu_event( - "PA1", - alice(), - EventType::RoomPowerLevels, - Some(""), - json!({ "users": { alice(): 100, bob(): 50 } }), - ), - to_init_pdu_event("T2", alice(), EventType::RoomTopic, Some(""), json!({})), - to_init_pdu_event( - "PA2", - alice(), - EventType::RoomPowerLevels, - Some(""), - json!({ "users": { alice(): 100, bob(): 0 } }), - ), - to_init_pdu_event( - "PB", - bob(), - EventType::RoomPowerLevels, - Some(""), - json!({ "users": { alice(): 100, bob(): 50 } }), - ), - to_init_pdu_event("T3", bob(), EventType::RoomTopic, Some(""), json!({})), - to_init_pdu_event("MZ1", zara(), EventType::RoomTopic, Some(""), json!({})), - to_init_pdu_event("T4", alice(), EventType::RoomTopic, Some(""), json!({})), - ]; - - let edges = vec![ - vec!["END", "T4", "MZ1", "PA2", "T2", "PA1", "T1", "START"], - vec!["END", "MZ1", "T3", "PB", "PA1"], - ] - .into_iter() - .map(|list| list.into_iter().map(event_id).collect::>()) - .collect::>(); - - let expected_state_ids = vec!["T4", "PA2"].into_iter().map(event_id).collect::>(); - - do_check(events, edges, expected_state_ids) -} - -#[test] -fn test_event_map_none() { - let _ = tracing::subscriber::set_default(tracing_subscriber::fmt().with_test_writer().finish()); - - let mut store = TestStore::(hashmap! {}); - - // build up the DAG - let (state_at_bob, state_at_charlie, expected) = store.set_up(); - - let ev_map: EventMap> = store.0.clone(); - let state_sets = vec![state_at_bob, state_at_charlie]; - let resolved = match state_res::resolve::( - &RoomVersionId::Version2, - &state_sets, - state_sets - .iter() - .map(|map| { - store - .auth_event_ids(&room_id(), &map.values().cloned().collect::>()) - .unwrap() - }) - .collect(), - |id| ev_map.get(id).map(Arc::clone), - ) { - Ok(state) => state, - Err(e) => panic!("{}", e), - }; - - assert_eq!(expected, resolved) -} - -#[test] -fn test_lexicographical_sort() { - let _ = tracing::subscriber::set_default(tracing_subscriber::fmt().with_test_writer().finish()); - - let graph = hashmap! { - event_id("l") => hashset![event_id("o")], - event_id("m") => hashset![event_id("n"), event_id("o")], - event_id("n") => hashset![event_id("o")], - event_id("o") => hashset![], // "o" has zero outgoing edges but 4 incoming edges - event_id("p") => hashset![event_id("o")], - }; - - let res = state_res::lexicographical_topological_sort(&graph, |id| { - Ok((0, MilliSecondsSinceUnixEpoch(uint!(0)), id.clone())) - }) - .unwrap(); - - assert_eq!( - vec!["o", "l", "n", "m", "p"], - res.iter() - .map(ToString::to_string) - .map(|s| s.replace("$", "").replace(":foo", "")) - .collect::>() - ) -}