From dd2a115b94de2add57ca57682118be7677ebcb19 Mon Sep 17 00:00:00 2001 From: Devin Ragotzy Date: Mon, 14 Dec 2020 13:28:28 -0500 Subject: [PATCH] Clean up tests, move setup into its own file --- tests/event_auth.rs | 234 +-------------- tests/event_sorting.rs | 217 +------------- tests/res_with_auth_ids.rs | 567 ++++++------------------------------- tests/state_res.rs | 487 +------------------------------ tests/utils.rs | 505 +++++++++++++++++++++++++++++++++ 5 files changed, 607 insertions(+), 1403 deletions(-) create mode 100644 tests/utils.rs diff --git a/tests/event_auth.rs b/tests/event_auth.rs index fe3df512..be5689da 100644 --- a/tests/event_auth.rs +++ b/tests/event_auth.rs @@ -1,242 +1,16 @@ -use std::{collections::BTreeMap, convert::TryFrom, sync::Arc}; +use std::sync::Arc; -use ruma::{ - events::{ - room::{ - join_rules::JoinRule, - member::{MemberEventContent, MembershipState}, - }, - EventType, - }, - identifiers::{EventId, RoomId, UserId}, -}; -use serde_json::{json, Value as JsonValue}; #[rustfmt::skip] // this deletes the comments for some reason yay! use state_res::{ event_auth::{ // auth_check, auth_types_for_event, can_federate, check_power_levels, check_redaction, valid_membership_change, }, - Requester, StateEvent, StateMap, StateStore, Result, Error + Requester, StateMap }; -use tracing_subscriber as tracer; -use std::sync::Once; - -static LOGGER: Once = Once::new(); - -static mut SERVER_TIMESTAMP: i32 = 0; - -fn event_id(id: &str) -> EventId { - if id.contains('$') { - return EventId::try_from(id).unwrap(); - } - EventId::try_from(format!("${}:foo", id)).unwrap() -} - -fn alice() -> UserId { - UserId::try_from("@alice:foo").unwrap() -} -fn bob() -> UserId { - UserId::try_from("@bob:foo").unwrap() -} -fn charlie() -> UserId { - UserId::try_from("@charlie:foo").unwrap() -} - -fn room_id() -> RoomId { - RoomId::try_from("!test:foo").unwrap() -} - -fn member_content_ban() -> JsonValue { - serde_json::to_value(MemberEventContent { - membership: MembershipState::Ban, - displayname: None, - avatar_url: None, - is_direct: None, - third_party_invite: None, - }) - .unwrap() -} - -fn member_content_join() -> JsonValue { - serde_json::to_value(MemberEventContent { - membership: MembershipState::Join, - displayname: None, - avatar_url: None, - is_direct: None, - third_party_invite: None, - }) - .unwrap() -} - -pub struct TestStore(BTreeMap>); - -#[allow(unused)] -impl StateStore for TestStore { - fn get_event(&self, room_id: &RoomId, event_id: &EventId) -> Result> { - self.0 - .get(event_id) - .map(Arc::clone) - .ok_or_else(|| Error::NotFound(format!("{} not found", event_id.to_string()))) - } -} - -fn to_pdu_event( - id: &str, - sender: UserId, - ev_type: EventType, - state_key: Option<&str>, - content: JsonValue, - auth_events: &[S], - prev_events: &[S], -) -> Arc -where - S: AsRef, -{ - let ts = unsafe { - let ts = SERVER_TIMESTAMP; - // increment the "origin_server_ts" value - SERVER_TIMESTAMP += 1; - ts - }; - let id = if id.contains('$') { - id.to_string() - } else { - format!("${}:foo", id) - }; - let auth_events = auth_events - .iter() - .map(AsRef::as_ref) - .map(event_id) - // .map(|id| { - // ( - // id, - // EventHash { - // sha256: "hello".into(), - // }, - // ) - // }) - .collect::>(); - let prev_events = prev_events - .iter() - .map(AsRef::as_ref) - .map(event_id) - // .map(|id| { - // ( - // id, - // EventHash { - // sha256: "hello".into(), - // }, - // ) - // }) - .collect::>(); - - let json = if let Some(state_key) = state_key { - json!({ - "auth_events": auth_events, - "prev_events": prev_events, - "event_id": id, - "sender": sender, - "type": ev_type, - "state_key": state_key, - "content": content, - "origin_server_ts": ts, - "room_id": room_id(), - "origin": "foo", - "depth": 0, - "hashes": { "sha256": "hello" }, - "signatures": {}, - }) - } else { - json!({ - "auth_events": auth_events, - "prev_events": prev_events, - "event_id": id, - "sender": sender, - "type": ev_type, - "content": content, - "origin_server_ts": ts, - "room_id": room_id(), - "origin": "foo", - "depth": 0, - "hashes": { "sha256": "hello" }, - "signatures": {}, - }) - }; - Arc::new(serde_json::from_value(json).unwrap()) -} - -// all graphs start with these input events -#[allow(non_snake_case)] -fn INITIAL_EVENTS() -> BTreeMap> { - // this is always called so we can init the logger here - let _ = LOGGER.call_once(|| { - tracer::fmt() - .with_env_filter(tracer::EnvFilter::from_default_env()) - .init() - }); - - vec![ - to_pdu_event::( - "CREATE", - alice(), - EventType::RoomCreate, - Some(""), - json!({ "creator": alice() }), - &[], - &[], - ), - to_pdu_event( - "IMA", - alice(), - EventType::RoomMember, - Some(alice().to_string().as_str()), - member_content_join(), - &["CREATE"], - &["CREATE"], - ), - to_pdu_event( - "IPOWER", - alice(), - EventType::RoomPowerLevels, - Some(""), - json!({"users": {alice().to_string(): 100}}), - &["CREATE", "IMA"], - &["IMA"], - ), - to_pdu_event( - "IJR", - alice(), - EventType::RoomJoinRules, - Some(""), - json!({ "join_rule": JoinRule::Public }), - &["CREATE", "IMA", "IPOWER"], - &["IPOWER"], - ), - to_pdu_event( - "IMB", - bob(), - EventType::RoomMember, - Some(bob().to_string().as_str()), - member_content_join(), - &["CREATE", "IJR", "IPOWER"], - &["IJR"], - ), - to_pdu_event( - "IMC", - charlie(), - EventType::RoomMember, - Some(charlie().to_string().as_str()), - member_content_join(), - &["CREATE", "IJR", "IPOWER"], - &["IMB"], - ), - ] - .into_iter() - .map(|ev| (ev.event_id(), ev)) - .collect() -} +mod utils; +use utils::{alice, charlie, event_id, member_content_ban, room_id, INITIAL_EVENTS}; #[test] fn test_ban_pass() { diff --git a/tests/event_sorting.rs b/tests/event_sorting.rs index 8897a4a2..6c49a153 100644 --- a/tests/event_sorting.rs +++ b/tests/event_sorting.rs @@ -1,217 +1,13 @@ -use std::{collections::BTreeMap, convert::TryFrom, sync::Arc}; +use std::collections::BTreeMap; use ruma::{ - events::{ - room::{ - join_rules::JoinRule, - member::{MemberEventContent, MembershipState}, - }, - EventType, - }, - identifiers::{EventId, RoomId, RoomVersionId, UserId}, + events::EventType, + identifiers::{EventId, RoomVersionId}, }; -use serde_json::{json, Value as JsonValue}; -use state_res::{Error, Result, StateEvent, StateMap, StateStore}; -use tracing_subscriber as tracer; +use state_res::StateMap; -use std::sync::Once; - -static LOGGER: Once = Once::new(); - -static mut SERVER_TIMESTAMP: i32 = 0; - -fn event_id(id: &str) -> EventId { - if id.contains('$') { - return EventId::try_from(id).unwrap(); - } - EventId::try_from(format!("${}:foo", id)).unwrap() -} - -fn alice() -> UserId { - UserId::try_from("@alice:foo").unwrap() -} -fn bob() -> UserId { - UserId::try_from("@bob:foo").unwrap() -} -fn charlie() -> UserId { - UserId::try_from("@charlie:foo").unwrap() -} - -fn room_id() -> RoomId { - RoomId::try_from("!test:foo").unwrap() -} - -fn member_content_join() -> JsonValue { - serde_json::to_value(MemberEventContent { - membership: MembershipState::Join, - displayname: None, - avatar_url: None, - is_direct: None, - third_party_invite: None, - }) - .unwrap() -} - -pub struct TestStore(BTreeMap>); - -#[allow(unused)] -impl StateStore for TestStore { - fn get_event(&self, room_id: &RoomId, event_id: &EventId) -> Result> { - self.0 - .get(event_id) - .map(Arc::clone) - .ok_or_else(|| Error::NotFound(format!("{} not found", event_id.to_string()))) - } -} - -fn to_pdu_event( - id: &str, - sender: UserId, - ev_type: EventType, - state_key: Option<&str>, - content: JsonValue, - auth_events: &[S], - prev_events: &[S], -) -> Arc -where - S: AsRef, -{ - let ts = unsafe { - let ts = SERVER_TIMESTAMP; - // increment the "origin_server_ts" value - SERVER_TIMESTAMP += 1; - ts - }; - let id = if id.contains('$') { - id.to_string() - } else { - format!("${}:foo", id) - }; - let auth_events = auth_events - .iter() - .map(AsRef::as_ref) - .map(event_id) - .collect::>(); - let prev_events = prev_events - .iter() - .map(AsRef::as_ref) - .map(event_id) - .collect::>(); - - let json = if let Some(state_key) = state_key { - json!({ - "auth_events": auth_events, - "prev_events": prev_events, - "event_id": id, - "sender": sender, - "type": ev_type, - "state_key": state_key, - "content": content, - "origin_server_ts": ts, - "room_id": room_id(), - "origin": "foo", - "depth": 0, - "hashes": { "sha256": "hello" }, - "signatures": {}, - }) - } else { - json!({ - "auth_events": auth_events, - "prev_events": prev_events, - "event_id": id, - "sender": sender, - "type": ev_type, - "content": content, - "origin_server_ts": ts, - "room_id": room_id(), - "origin": "foo", - "depth": 0, - "hashes": { "sha256": "hello" }, - "signatures": {}, - }) - }; - Arc::new(serde_json::from_value(json).unwrap()) -} - -// all graphs start with these input events -#[allow(non_snake_case)] -fn INITIAL_EVENTS() -> BTreeMap> { - // this is always called so we can init the logger here - let _ = LOGGER.call_once(|| { - tracer::fmt() - .with_env_filter(tracer::EnvFilter::from_default_env()) - .init() - }); - - vec![ - to_pdu_event::( - "CREATE", - alice(), - EventType::RoomCreate, - Some(""), - json!({ "creator": alice() }), - &[], - &[], - ), - to_pdu_event( - "IMA", - alice(), - EventType::RoomMember, - Some(alice().to_string().as_str()), - member_content_join(), - &["CREATE"], - &["CREATE"], - ), - to_pdu_event( - "IPOWER", - alice(), - EventType::RoomPowerLevels, - Some(""), - json!({"users": {alice().to_string(): 100}}), - &["CREATE", "IMA"], - &["IMA"], - ), - to_pdu_event( - "IJR", - alice(), - EventType::RoomJoinRules, - Some(""), - json!({ "join_rule": JoinRule::Public }), - &["CREATE", "IMA", "IPOWER"], - &["IPOWER"], - ), - to_pdu_event( - "IMB", - bob(), - EventType::RoomMember, - Some(bob().to_string().as_str()), - member_content_join(), - &["CREATE", "IJR", "IPOWER"], - &["IJR"], - ), - to_pdu_event( - "IMC", - charlie(), - EventType::RoomMember, - Some(charlie().to_string().as_str()), - member_content_join(), - &["CREATE", "IJR", "IPOWER"], - &["IMB"], - ), - to_pdu_event::( - "END", - charlie(), - EventType::RoomTopic, - Some(""), - json!({}), - &[], - &[], - ), - ] - .into_iter() - .map(|ev| (ev.event_id(), ev)) - .collect() -} +mod utils; +use utils::{room_id, TestStore, INITIAL_EVENTS}; fn shuffle(list: &mut [EventId]) { use rand::Rng; @@ -286,6 +82,7 @@ fn test_event_sort() { "$IJR:foo", "$IMB:foo", "$IMC:foo", + "$START:foo", "$END:foo" ], sorted_event_ids diff --git a/tests/res_with_auth_ids.rs b/tests/res_with_auth_ids.rs index e5e22e1e..eabb201b 100644 --- a/tests/res_with_auth_ids.rs +++ b/tests/res_with_auth_ids.rs @@ -1,471 +1,19 @@ #![allow(clippy::or_fun_call, clippy::expect_fun_call)] -use std::{ - collections::BTreeMap, - convert::TryFrom, - sync::{Arc, Once}, - time::UNIX_EPOCH, -}; +use std::{collections::BTreeMap, sync::Arc}; use ruma::{ - events::{ - room::{ - join_rules::JoinRule, - member::{MemberEventContent, MembershipState}, - }, - EventType, - }, - identifiers::{EventId, RoomId, RoomVersionId, UserId}, + events::EventType, + identifiers::{EventId, RoomVersionId}, }; -use serde_json::{json, Value as JsonValue}; -use state_res::{Error, Result, StateEvent, StateMap, StateResolution, StateStore}; -use tracing_subscriber as tracer; +use serde_json::json; +use state_res::{StateEvent, StateMap, StateResolution}; -static LOGGER: Once = Once::new(); - -static mut SERVER_TIMESTAMP: i32 = 0; - -fn do_check( - events: &[Arc], - edges: Vec>, - expected_state_ids: Vec, -) { - // to activate logging use `RUST_LOG=debug cargo t` - let _ = LOGGER.call_once(|| { - tracer::fmt() - .with_env_filter(tracer::EnvFilter::from_default_env()) - .init() - }); - - let mut store = TestStore( - INITIAL_EVENTS() - .values() - .chain(events) - .map(|ev| (ev.event_id(), ev.clone())) - .collect(), - ); - - // This will be lexi_topo_sorted for resolution - let mut graph = BTreeMap::new(); - // this is the same as in `resolve` event_id -> StateEvent - let mut fake_event_map = BTreeMap::new(); - - // create the DB of events that led up to this point - // TODO maybe clean up some of these clones it is just tests but... - for ev in INITIAL_EVENTS().values().chain(events) { - graph.insert(ev.event_id().clone(), vec![]); - fake_event_map.insert(ev.event_id().clone(), ev.clone()); - } - - for pair in INITIAL_EDGES().windows(2) { - if let [a, b] = &pair { - graph.entry(a.clone()).or_insert(vec![]).push(b.clone()); - } - } - - for edge_list in edges { - for pair in edge_list.windows(2) { - if let [a, b] = &pair { - graph.entry(a.clone()).or_insert(vec![]).push(b.clone()); - } - } - } - - // event_id -> StateEvent - let mut event_map: BTreeMap> = BTreeMap::new(); - // event_id -> StateMap - let mut state_at_event: BTreeMap> = BTreeMap::new(); - - // resolve the current state and add it to the state_at_event map then continue - // on in "time" - for node in - StateResolution::lexicographical_topological_sort(&graph, |id| (0, UNIX_EPOCH, id.clone())) - { - let fake_event = fake_event_map.get(&node).unwrap(); - let event_id = fake_event.event_id(); - - let prev_events = graph.get(&node).unwrap(); - - let state_before: StateMap = if prev_events.is_empty() { - BTreeMap::new() - } else if prev_events.len() == 1 { - state_at_event.get(&prev_events[0]).unwrap().clone() - } else { - let state_sets = prev_events - .iter() - .filter_map(|k| state_at_event.get(k)) - .cloned() - .collect::>(); - - tracing::info!( - "{:#?}", - state_sets - .iter() - .map(|map| map - .iter() - .map(|((ty, key), id)| format!("(({}{:?}), {})", ty, key, id)) - .collect::>()) - .collect::>() - ); - - let resolved = StateResolution::resolve( - &room_id(), - &RoomVersionId::Version1, - &state_sets, - Some(event_map.clone()), - &store, - ); - match resolved { - Ok(state) => state, - Err(e) => panic!("resolution for {} failed: {}", node, e), - } - }; - - let mut state_after = state_before.clone(); - - // if fake_event.state_key().is_some() { - let ty = fake_event.kind().clone(); - // we know there is a state_key unwrap OK - let key = fake_event.state_key().clone(); - state_after.insert((ty, key), event_id.clone()); - // } - - let auth_types = state_res::auth_types_for_event( - fake_event.kind(), - fake_event.sender(), - Some(fake_event.state_key()), - fake_event.content().clone(), - ); - - let mut auth_events = vec![]; - for key in auth_types { - if state_before.contains_key(&key) { - auth_events.push(state_before[&key].clone()) - } - } - - // TODO The event is just remade, adding the auth_events and prev_events here - // the `to_pdu_event` was split into `init` and the fn below, could be better - let e = fake_event; - let ev_id = e.event_id(); - let event = to_pdu_event( - &e.event_id().to_string(), - e.sender().clone(), - e.kind(), - Some(e.state_key()).as_deref(), - e.content().clone(), - &auth_events, - prev_events, - ); - - // we have to update our store, an actual user of this lib would - // be giving us state from a DB. - store.0.insert(ev_id.clone(), event.clone()); - - state_at_event.insert(node, state_after); - event_map.insert(event_id.clone(), Arc::clone(store.0.get(&ev_id).unwrap())); - } - - let mut expected_state = StateMap::new(); - for node in expected_state_ids { - let ev = event_map.get(&node).expect(&format!( - "{} not found in {:?}", - node.to_string(), - event_map - .keys() - .map(ToString::to_string) - .collect::>(), - )); - - let key = (ev.kind(), ev.state_key()); - - expected_state.insert(key, node); - } - - let start_state = state_at_event.get(&event_id("$START:foo")).unwrap(); - - let end_state = state_at_event - .get(&event_id("$END:foo")) - .unwrap() - .iter() - .filter(|(k, v)| expected_state.contains_key(k) || start_state.get(k) != Some(*v)) - .map(|(k, v)| (k.clone(), v.clone())) - .collect::>(); - - assert_eq!(expected_state, end_state); -} -pub struct TestStore(BTreeMap>); - -#[allow(unused)] -impl StateStore for TestStore { - fn get_event(&self, room_id: &RoomId, event_id: &EventId) -> Result> { - self.0 - .get(event_id) - .map(Arc::clone) - .ok_or_else(|| Error::NotFound(format!("{} not found", event_id.to_string()))) - } -} - -fn event_id(id: &str) -> EventId { - if id.contains('$') { - return EventId::try_from(id).unwrap(); - } - EventId::try_from(format!("${}:foo", id)).unwrap() -} - -fn alice() -> UserId { - UserId::try_from("@alice:foo").unwrap() -} -fn bob() -> UserId { - UserId::try_from("@bob:foo").unwrap() -} -fn charlie() -> UserId { - UserId::try_from("@charlie:foo").unwrap() -} -fn ella() -> UserId { - UserId::try_from("@ella:foo").unwrap() -} -fn zara() -> UserId { - UserId::try_from("@zara:foo").unwrap() -} - -fn room_id() -> RoomId { - RoomId::try_from("!test:foo").unwrap() -} - -fn member_content_ban() -> JsonValue { - serde_json::to_value(MemberEventContent { - membership: MembershipState::Ban, - displayname: None, - avatar_url: None, - is_direct: None, - third_party_invite: None, - }) - .unwrap() -} - -fn member_content_join() -> JsonValue { - serde_json::to_value(MemberEventContent { - membership: MembershipState::Join, - displayname: None, - avatar_url: None, - is_direct: None, - third_party_invite: None, - }) - .unwrap() -} - -fn to_pdu_event( - id: &str, - sender: UserId, - ev_type: EventType, - state_key: Option<&str>, - content: JsonValue, - auth_events: &[S], - prev_events: &[S], -) -> Arc -where - S: AsRef, -{ - let ts = unsafe { - let ts = SERVER_TIMESTAMP; - // increment the "origin_server_ts" value - SERVER_TIMESTAMP += 1; - ts - }; - let id = if id.contains('$') { - id.to_string() - } else { - format!("${}:foo", id) - }; - let auth_events = auth_events - .iter() - .map(AsRef::as_ref) - .map(event_id) - .collect::>(); - let prev_events = prev_events - .iter() - .map(AsRef::as_ref) - .map(event_id) - .collect::>(); - - let json = if let Some(state_key) = state_key { - json!({ - "auth_events": auth_events, - "prev_events": prev_events, - "event_id": id, - "sender": sender, - "type": ev_type, - "state_key": state_key, - "content": content, - "origin_server_ts": ts, - "room_id": room_id(), - "origin": "foo", - "depth": 0, - "hashes": { "sha256": "hello" }, - "signatures": {}, - }) - } else { - json!({ - "auth_events": auth_events, - "prev_events": prev_events, - "event_id": id, - "sender": sender, - "type": ev_type, - "content": content, - "origin_server_ts": ts, - "room_id": room_id(), - "origin": "foo", - "depth": 0, - "hashes": { "sha256": "hello" }, - "signatures": {}, - }) - }; - Arc::new(serde_json::from_value(json).unwrap()) -} - -// all graphs start with these input events -#[allow(non_snake_case)] -fn INITIAL_EVENTS() -> BTreeMap> { - // this is always called so we can init the logger here - let _ = LOGGER.call_once(|| { - tracer::fmt() - .with_env_filter(tracer::EnvFilter::from_default_env()) - .init() - }); - - vec![ - to_pdu_event::( - "CREATE", - alice(), - EventType::RoomCreate, - Some(""), - json!({ "creator": alice() }), - &[], - &[], - ), - to_pdu_event( - "IMA", - alice(), - EventType::RoomMember, - Some(alice().to_string().as_str()), - member_content_join(), - &["CREATE"], - &["CREATE"], - ), - to_pdu_event( - "IPOWER", - alice(), - EventType::RoomPowerLevels, - Some(""), - json!({"users": {alice().to_string(): 100}}), - &["CREATE", "IMA"], - &["IMA"], - ), - to_pdu_event( - "IJR", - alice(), - EventType::RoomJoinRules, - Some(""), - json!({ "join_rule": JoinRule::Public }), - &["CREATE", "IMA", "IPOWER"], - &["IPOWER"], - ), - to_pdu_event( - "IMB", - bob(), - EventType::RoomMember, - Some(bob().to_string().as_str()), - member_content_join(), - &["CREATE", "IJR", "IPOWER"], - &["IJR"], - ), - to_pdu_event( - "IMC", - charlie(), - EventType::RoomMember, - Some(charlie().to_string().as_str()), - member_content_join(), - &["CREATE", "IJR", "IPOWER"], - &["IMB"], - ), - to_pdu_event::( - "START", - charlie(), - EventType::RoomTopic, - Some(""), - json!({}), - &[], - &[], - ), - to_pdu_event::( - "END", - charlie(), - EventType::RoomTopic, - Some(""), - json!({}), - &[], - &[], - ), - ] - .into_iter() - .map(|ev| (ev.event_id(), ev)) - .collect() -} - -#[allow(non_snake_case)] -fn INITIAL_EDGES() -> Vec { - vec!["START", "IMC", "IMB", "IJR", "IPOWER", "IMA", "CREATE"] - .into_iter() - .map(event_id) - .collect::>() -} - -// all graphs start with these input events -#[allow(non_snake_case)] -fn BAN_STATE_SET() -> BTreeMap> { - 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(), ev)) - .collect() -} +mod utils; +use utils::{ + alice, bob, do_check, ella, event_id, member_content_ban, member_content_join, room_id, + to_pdu_event, zara, TestStore, INITIAL_EVENTS, +}; #[test] fn ban_with_auth_chains() { @@ -488,12 +36,13 @@ fn ban_with_auth_chains() { ); } +// Sanity check that the store is able to fetch auth chain and such #[test] fn base_with_auth_chains() { let store = TestStore(INITIAL_EVENTS()); let resolved: BTreeMap<_, EventId> = - match StateResolution::resolve(&room_id(), &RoomVersionId::Version2, &[], None, &store) { + match StateResolution::resolve(&room_id(), &RoomVersionId::Version6, &[], None, &store) { Ok(state) => state, Err(e) => panic!("{}", e), }; @@ -558,7 +107,7 @@ fn ban_with_auth_chains2() { let resolved: StateMap = match StateResolution::resolve( &room_id(), - &RoomVersionId::Version2, + &RoomVersionId::Version6, &[state_set_a, state_set_b], None, &store, @@ -586,7 +135,7 @@ fn ban_with_auth_chains2() { ]; for id in expected.iter().map(|i| event_id(i)) { - // make sure our resolved events are equall to the expected list + // make sure our resolved events are equal to the expected list assert!( resolved.values().any(|eid| eid == &id) || init.contains_key(&id), "{}", @@ -596,7 +145,72 @@ fn ban_with_auth_chains2() { assert_eq!(expected.len(), resolved.len()) } -// all graphs start with these input events +#[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", "END"] + .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() -> BTreeMap> { + 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(), ev)) + .collect() +} + #[allow(non_snake_case)] fn JOIN_RULE() -> BTreeMap> { vec![ @@ -623,24 +237,3 @@ fn JOIN_RULE() -> BTreeMap> { .map(|ev| (ev.event_id(), ev)) .collect() } - -#[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", "END"] - .into_iter() - .map(event_id) - .collect::>(); - - do_check( - &join_rule.values().cloned().collect::>(), - edges, - expected_state_ids, - ); -} diff --git a/tests/state_res.rs b/tests/state_res.rs index 00efa3a4..f7c69fe2 100644 --- a/tests/state_res.rs +++ b/tests/state_res.rs @@ -1,470 +1,19 @@ -use std::{collections::BTreeMap, convert::TryFrom, sync::Arc, time::UNIX_EPOCH}; +use std::{sync::Arc, time::UNIX_EPOCH}; use maplit::btreemap; use ruma::{ - events::{ - room::{ - join_rules::JoinRule, - member::{MemberEventContent, MembershipState}, - }, - EventType, - }, - identifiers::{EventId, RoomId, RoomVersionId, UserId}, + events::{room::join_rules::JoinRule, EventType}, + identifiers::{EventId, RoomVersionId}, }; -use serde_json::{json, Value as JsonValue}; -use state_res::{Error, Result, StateEvent, StateMap, StateResolution, StateStore}; +use serde_json::json; +use state_res::{StateMap, StateResolution}; use tracing_subscriber as tracer; -use std::sync::Once; - -static LOGGER: Once = Once::new(); - -static mut SERVER_TIMESTAMP: i32 = 0; - -fn event_id(id: &str) -> EventId { - if id.contains('$') { - return EventId::try_from(id).unwrap(); - } - EventId::try_from(format!("${}:foo", id)).unwrap() -} - -fn alice() -> UserId { - UserId::try_from("@alice:foo").unwrap() -} -fn bob() -> UserId { - UserId::try_from("@bob:foo").unwrap() -} -fn charlie() -> UserId { - UserId::try_from("@charlie:foo").unwrap() -} -fn ella() -> UserId { - UserId::try_from("@ella:foo").unwrap() -} -fn zera() -> UserId { - UserId::try_from("@zera:foo").unwrap() -} - -fn room_id() -> RoomId { - RoomId::try_from("!test:foo").unwrap() -} - -fn member_content_ban() -> JsonValue { - serde_json::to_value(MemberEventContent { - membership: MembershipState::Ban, - displayname: None, - avatar_url: None, - is_direct: None, - third_party_invite: None, - }) - .unwrap() -} -fn member_content_join() -> JsonValue { - serde_json::to_value(MemberEventContent { - membership: MembershipState::Join, - displayname: None, - avatar_url: None, - is_direct: None, - third_party_invite: None, - }) - .unwrap() -} - -fn to_pdu_event( - id: &str, - sender: UserId, - ev_type: EventType, - state_key: Option<&str>, - content: JsonValue, - auth_events: &[S], - prev_events: &[S], -) -> Arc -where - S: AsRef, -{ - let ts = unsafe { - let ts = SERVER_TIMESTAMP; - // increment the "origin_server_ts" value - SERVER_TIMESTAMP += 1; - ts - }; - let id = if id.contains('$') { - id.to_string() - } else { - format!("${}:foo", id) - }; - let auth_events = auth_events - .iter() - .map(AsRef::as_ref) - .map(event_id) - .collect::>(); - let prev_events = prev_events - .iter() - .map(AsRef::as_ref) - .map(event_id) - .collect::>(); - - let json = if let Some(state_key) = state_key { - json!({ - "auth_events": auth_events, - "prev_events": prev_events, - "event_id": id, - "sender": sender, - "type": ev_type, - "state_key": state_key, - "content": content, - "origin_server_ts": ts, - "room_id": room_id(), - "origin": "foo", - "depth": 0, - "hashes": { "sha256": "hello" }, - "signatures": {}, - }) - } else { - json!({ - "auth_events": auth_events, - "prev_events": prev_events, - "event_id": id, - "sender": sender, - "type": ev_type, - "content": content, - "origin_server_ts": ts, - "room_id": room_id(), - "origin": "foo", - "depth": 0, - "hashes": { "sha256": "hello" }, - "signatures": {}, - }) - }; - Arc::new(serde_json::from_value(json).unwrap()) -} - -fn to_init_pdu_event( - id: &str, - sender: UserId, - ev_type: EventType, - state_key: Option<&str>, - content: JsonValue, -) -> Arc { - let ts = unsafe { - let ts = SERVER_TIMESTAMP; - // increment the "origin_server_ts" value - SERVER_TIMESTAMP += 1; - ts - }; - let id = if id.contains('$') { - id.to_string() - } else { - format!("${}:foo", id) - }; - - let json = if let Some(state_key) = state_key { - json!({ - "auth_events": [], - "prev_events": [], - "event_id": id, - "sender": sender, - "type": ev_type, - "state_key": state_key, - "content": content, - "origin_server_ts": ts, - "room_id": room_id(), - "origin": "foo", - "depth": 0, - "hashes": { "sha256": "hello" }, - "signatures": {}, - }) - } else { - json!({ - "auth_events": [], - "prev_events": [], - "event_id": id, - "sender": sender, - "type": ev_type, - "content": content, - "origin_server_ts": ts, - "room_id": room_id(), - "origin": "foo", - "depth": 0, - "hashes": { "sha256": "hello" }, - "signatures": {}, - }) - }; - Arc::new(serde_json::from_value(json).unwrap()) -} - -// all graphs start with these input events -#[allow(non_snake_case)] -fn INITIAL_EVENTS() -> BTreeMap> { - vec![ - to_init_pdu_event( - "CREATE", - alice(), - EventType::RoomCreate, - Some(""), - json!({ "creator": alice() }), - ), - to_init_pdu_event( - "IMA", - alice(), - EventType::RoomMember, - Some(alice().to_string().as_str()), - member_content_join(), - ), - to_init_pdu_event( - "IPOWER", - alice(), - EventType::RoomPowerLevels, - Some(""), - json!({"users": {alice().to_string(): 100}}), - ), - to_init_pdu_event( - "IJR", - alice(), - EventType::RoomJoinRules, - Some(""), - json!({ "join_rule": JoinRule::Public }), - ), - to_init_pdu_event( - "IMB", - bob(), - EventType::RoomMember, - Some(bob().to_string().as_str()), - member_content_join(), - ), - to_init_pdu_event( - "IMC", - charlie(), - EventType::RoomMember, - Some(charlie().to_string().as_str()), - member_content_join(), - ), - to_init_pdu_event( - "IMZ", - zera(), - EventType::RoomMember, - Some(zera().to_string().as_str()), - member_content_join(), - ), - to_init_pdu_event("START", zera(), EventType::RoomTopic, Some(""), json!({})), - to_init_pdu_event("END", zera(), EventType::RoomTopic, Some(""), json!({})), - ] - .into_iter() - .map(|ev| (ev.event_id(), ev)) - .collect() -} - -#[allow(non_snake_case)] -fn INITIAL_EDGES() -> Vec { - vec![ - "START", "IMZ", "IMC", "IMB", "IJR", "IPOWER", "IMA", "CREATE", - ] - .into_iter() - .map(event_id) - .collect::>() -} - -fn do_check( - events: &[Arc], - edges: Vec>, - expected_state_ids: Vec, -) { - // to activate logging use `RUST_LOG=debug cargo t one_test_only` - let _ = LOGGER.call_once(|| { - tracer::fmt() - .with_env_filter(tracer::EnvFilter::from_default_env()) - .init() - }); - - let mut store = TestStore( - INITIAL_EVENTS() - .values() - .chain(events) - .map(|ev| (ev.event_id(), ev.clone())) - .collect(), - ); - - // This will be lexi_topo_sorted for resolution - let mut graph = BTreeMap::new(); - // this is the same as in `resolve` event_id -> StateEvent - let mut fake_event_map = BTreeMap::new(); - - // create the DB of events that led up to this point - // TODO maybe clean up some of these clones it is just tests but... - for ev in INITIAL_EVENTS().values().chain(events) { - graph.insert(ev.event_id(), vec![]); - fake_event_map.insert(ev.event_id(), ev.clone()); - } - - for pair in INITIAL_EDGES().windows(2) { - if let [a, b] = &pair { - graph - .entry(a.clone()) - .or_insert_with(Vec::new) - .push(b.clone()); - } - } - - for edge_list in edges { - for pair in edge_list.windows(2) { - if let [a, b] = &pair { - graph - .entry(a.clone()) - .or_insert_with(Vec::new) - .push(b.clone()); - } - } - } - - // event_id -> StateEvent - let mut event_map: BTreeMap> = BTreeMap::new(); - // event_id -> StateMap - let mut state_at_event: BTreeMap> = BTreeMap::new(); - - // resolve the current state and add it to the state_at_event map then continue - // on in "time" - for node in - StateResolution::lexicographical_topological_sort(&graph, |id| (0, UNIX_EPOCH, id.clone())) - { - let fake_event = fake_event_map.get(&node).unwrap(); - let event_id = fake_event.event_id(); - - let prev_events = graph.get(&node).unwrap(); - - let state_before: StateMap = if prev_events.is_empty() { - BTreeMap::new() - } else if prev_events.len() == 1 { - state_at_event.get(&prev_events[0]).unwrap().clone() - } else { - let state_sets = prev_events - .iter() - .filter_map(|k| state_at_event.get(k)) - .cloned() - .collect::>(); - - tracing::debug!( - "{:#?}", - state_sets - .iter() - .map(|map| map - .iter() - .map(|((ty, key), id)| format!("(({}{:?}), {})", ty, key, id)) - .collect::>()) - .collect::>() - ); - - let resolved = StateResolution::resolve( - &room_id(), - &RoomVersionId::Version6, - &state_sets, - Some(event_map.clone()), - &store, - ); - match resolved { - Ok(state) => state, - Err(e) => panic!("resolution for {} failed: {}", node, e), - } - }; - - let mut state_after = state_before.clone(); - - let ty = fake_event.kind().clone(); - let key = fake_event.state_key().clone(); - state_after.insert((ty, key), event_id.clone()); - - let auth_types = state_res::auth_types_for_event( - fake_event.kind(), - fake_event.sender(), - Some(fake_event.state_key()), - fake_event.content().clone(), - ); - - let mut auth_events = vec![]; - for key in auth_types { - if state_before.contains_key(&key) { - auth_events.push(state_before[&key].clone()) - } - } - - // TODO The event is just remade, adding the auth_events and prev_events here - // UPDATE: the `to_pdu_event` was split into `init` and the fn below, could be better - let e = fake_event; - let ev_id = e.event_id(); - let event = to_pdu_event( - &e.event_id().to_string(), - e.sender().clone(), - e.kind(), - Some(e.state_key()).as_deref(), - e.content().clone(), - &auth_events, - prev_events, - ); - - // This can be used to sort of test this function - // match StateResolution::apply_event( - // &room_id(), - // &RoomVersionId::Version6, - // Arc::clone(&event), - // &state_after, - // Some(event_map.clone()), - // &store, - // ) { - // Ok(res) => { - // println!( - // "res contains: {} passed: {} for {}\n{:?}", - // state_after - // .get(&(event.kind(), event.state_key())) - // .map(|id| id == &ev_id) - // .unwrap_or_default(), - // res, - // event.event_id().as_str(), - // event - // .prev_event_ids() - // .iter() - // .map(|id| id.to_string()) - // .collect::>() - // ); - // } - // Err(e) => panic!("resolution for {} failed: {}", node, e), - // } - - // we have to update our store, an actual user of this lib would - // be giving us state from a DB. - store.0.insert(ev_id.clone(), Arc::clone(&event)); - - state_at_event.insert(node, state_after); - event_map.insert(event_id.clone(), event); - } - - let mut expected_state = StateMap::new(); - for node in expected_state_ids { - let ev = event_map.get(&node).unwrap_or_else(|| { - panic!( - "{} not found in {:?}", - node.to_string(), - event_map - .keys() - .map(ToString::to_string) - .collect::>(), - ) - }); - - let key = (ev.kind(), ev.state_key()); - - expected_state.insert(key, node); - } - - let start_state = state_at_event.get(&event_id("$START:foo")).unwrap(); - - let end_state = state_at_event - .get(&event_id("$END:foo")) - .unwrap() - .iter() - .filter(|(k, v)| expected_state.contains_key(k) || start_state.get(k) != Some(*v)) - .map(|(k, v)| (k.clone(), v.clone())) - .collect::>(); - - assert_eq!(expected_state, end_state); -} +mod utils; +use 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, TestStore, LOGGER, +}; #[test] fn ban_vs_power_level() { @@ -692,7 +241,7 @@ fn topic_setting() { json!({"users": {alice(): 100, bob(): 50}}), ), to_init_pdu_event("T3", bob(), EventType::RoomTopic, Some(""), json!({})), - to_init_pdu_event("MZ1", zera(), EventType::RoomTopic, Some(""), json!({})), + to_init_pdu_event("MZ1", zara(), EventType::RoomTopic, Some(""), json!({})), to_init_pdu_event("T4", alice(), EventType::RoomTopic, Some(""), json!({})), ]; @@ -758,20 +307,6 @@ fn test_lexicographical_sort() { // A StateStore implementation for testing // // - -/// The test state store. -pub struct TestStore(BTreeMap>); - -#[allow(unused)] -impl StateStore for TestStore { - fn get_event(&self, room_id: &RoomId, event_id: &EventId) -> Result> { - self.0 - .get(event_id) - .map(Arc::clone) - .ok_or_else(|| Error::NotFound(format!("{} not found", event_id.to_string()))) - } -} - impl TestStore { pub fn set_up(&mut self) -> (StateMap, StateMap, StateMap) { // to activate logging use `RUST_LOG=debug cargo t one_test_only` diff --git a/tests/utils.rs b/tests/utils.rs new file mode 100644 index 00000000..51548078 --- /dev/null +++ b/tests/utils.rs @@ -0,0 +1,505 @@ +#![allow(clippy::or_fun_call, clippy::expect_fun_call, dead_code)] + +use std::{ + collections::BTreeMap, + convert::TryFrom, + sync::{Arc, Once}, + time::UNIX_EPOCH, +}; + +use ruma::{ + events::{ + room::{ + join_rules::JoinRule, + member::{MemberEventContent, MembershipState}, + }, + EventType, + }, + identifiers::{EventId, RoomId, RoomVersionId, UserId}, +}; +use serde_json::{json, Value as JsonValue}; +use state_res::{Error, Result, StateEvent, StateMap, StateResolution, StateStore}; +use tracing_subscriber as tracer; + +pub static LOGGER: Once = Once::new(); + +static mut SERVER_TIMESTAMP: i32 = 0; + +pub fn do_check( + events: &[Arc], + edges: Vec>, + expected_state_ids: Vec, +) { + // to activate logging use `RUST_LOG=debug cargo t` + let _ = LOGGER.call_once(|| { + tracer::fmt() + .with_env_filter(tracer::EnvFilter::from_default_env()) + .init() + }); + + let mut store = TestStore( + INITIAL_EVENTS() + .values() + .chain(events) + .map(|ev| (ev.event_id(), ev.clone())) + .collect(), + ); + + // This will be lexi_topo_sorted for resolution + let mut graph = BTreeMap::new(); + // this is the same as in `resolve` event_id -> StateEvent + let mut fake_event_map = BTreeMap::new(); + + // create the DB of events that led up to this point + // TODO maybe clean up some of these clones it is just tests but... + for ev in INITIAL_EVENTS().values().chain(events) { + graph.insert(ev.event_id().clone(), vec![]); + fake_event_map.insert(ev.event_id().clone(), ev.clone()); + } + + for pair in INITIAL_EDGES().windows(2) { + if let [a, b] = &pair { + graph.entry(a.clone()).or_insert(vec![]).push(b.clone()); + } + } + + for edge_list in edges { + for pair in edge_list.windows(2) { + if let [a, b] = &pair { + graph.entry(a.clone()).or_insert(vec![]).push(b.clone()); + } + } + } + + // event_id -> StateEvent + let mut event_map: BTreeMap> = BTreeMap::new(); + // event_id -> StateMap + let mut state_at_event: BTreeMap> = BTreeMap::new(); + + // resolve the current state and add it to the state_at_event map then continue + // on in "time" + for node in + StateResolution::lexicographical_topological_sort(&graph, |id| (0, UNIX_EPOCH, id.clone())) + { + let fake_event = fake_event_map.get(&node).unwrap(); + let event_id = fake_event.event_id(); + + let prev_events = graph.get(&node).unwrap(); + + let state_before: StateMap = if prev_events.is_empty() { + BTreeMap::new() + } else if prev_events.len() == 1 { + state_at_event.get(&prev_events[0]).unwrap().clone() + } else { + let state_sets = prev_events + .iter() + .filter_map(|k| state_at_event.get(k)) + .cloned() + .collect::>(); + + tracing::info!( + "{:#?}", + state_sets + .iter() + .map(|map| map + .iter() + .map(|((ty, key), id)| format!("(({}{:?}), {})", ty, key, id)) + .collect::>()) + .collect::>() + ); + + let resolved = StateResolution::resolve( + &room_id(), + &RoomVersionId::Version1, + &state_sets, + Some(event_map.clone()), + &store, + ); + match resolved { + Ok(state) => state, + Err(e) => panic!("resolution for {} failed: {}", node, e), + } + }; + + let mut state_after = state_before.clone(); + + // if fake_event.state_key().is_some() { + let ty = fake_event.kind().clone(); + // we know there is a state_key unwrap OK + let key = fake_event.state_key().clone(); + state_after.insert((ty, key), event_id.clone()); + // } + + let auth_types = state_res::auth_types_for_event( + fake_event.kind(), + fake_event.sender(), + Some(fake_event.state_key()), + fake_event.content().clone(), + ); + + let mut auth_events = vec![]; + for key in auth_types { + if state_before.contains_key(&key) { + auth_events.push(state_before[&key].clone()) + } + } + + // TODO The event is just remade, adding the auth_events and prev_events here + // the `to_pdu_event` was split into `init` and the fn below, could be better + let e = fake_event; + let ev_id = e.event_id(); + let event = to_pdu_event( + &e.event_id().to_string(), + e.sender().clone(), + e.kind(), + Some(e.state_key()).as_deref(), + e.content().clone(), + &auth_events, + prev_events, + ); + + // This can be used to sort of test this function + // match StateResolution::apply_event( + // &room_id(), + // &RoomVersionId::Version6, + // Arc::clone(&event), + // &state_after, + // Some(event_map.clone()), + // &store, + // ) { + // Ok(res) => { + // println!( + // "res contains: {} passed: {} for {}\n{:?}", + // state_after + // .get(&(event.kind(), event.state_key())) + // .map(|id| id == &ev_id) + // .unwrap_or_default(), + // res, + // event.event_id().as_str(), + // event + // .prev_event_ids() + // .iter() + // .map(|id| id.to_string()) + // .collect::>() + // ); + // } + // Err(e) => panic!("resolution for {} failed: {}", node, e), + // } + + // we have to update our store, an actual user of this lib would + // be giving us state from a DB. + store.0.insert(ev_id.clone(), event.clone()); + + state_at_event.insert(node, state_after); + event_map.insert(event_id.clone(), Arc::clone(store.0.get(&ev_id).unwrap())); + } + + let mut expected_state = StateMap::new(); + for node in expected_state_ids { + let ev = event_map.get(&node).expect(&format!( + "{} not found in {:?}", + node.to_string(), + event_map + .keys() + .map(ToString::to_string) + .collect::>(), + )); + + let key = (ev.kind(), ev.state_key()); + + expected_state.insert(key, node); + } + + let start_state = state_at_event.get(&event_id("$START:foo")).unwrap(); + + let end_state = state_at_event + .get(&event_id("$END:foo")) + .unwrap() + .iter() + .filter(|(k, v)| expected_state.contains_key(k) || start_state.get(k) != Some(*v)) + .map(|(k, v)| (k.clone(), v.clone())) + .collect::>(); + + assert_eq!(expected_state, end_state); +} + +pub struct TestStore(pub BTreeMap>); + +#[allow(unused)] +impl StateStore for TestStore { + fn get_event(&self, room_id: &RoomId, event_id: &EventId) -> Result> { + self.0 + .get(event_id) + .map(Arc::clone) + .ok_or_else(|| Error::NotFound(format!("{} not found", event_id.to_string()))) + } +} + +pub fn event_id(id: &str) -> EventId { + if id.contains('$') { + return EventId::try_from(id).unwrap(); + } + EventId::try_from(format!("${}:foo", id)).unwrap() +} + +pub fn alice() -> UserId { + UserId::try_from("@alice:foo").unwrap() +} +pub fn bob() -> UserId { + UserId::try_from("@bob:foo").unwrap() +} +pub fn charlie() -> UserId { + UserId::try_from("@charlie:foo").unwrap() +} +pub fn ella() -> UserId { + UserId::try_from("@ella:foo").unwrap() +} +pub fn zara() -> UserId { + UserId::try_from("@zara:foo").unwrap() +} + +pub fn room_id() -> RoomId { + RoomId::try_from("!test:foo").unwrap() +} + +pub fn member_content_ban() -> JsonValue { + serde_json::to_value(MemberEventContent { + membership: MembershipState::Ban, + displayname: None, + avatar_url: None, + is_direct: None, + third_party_invite: None, + }) + .unwrap() +} + +pub fn member_content_join() -> JsonValue { + serde_json::to_value(MemberEventContent { + membership: MembershipState::Join, + displayname: None, + avatar_url: None, + is_direct: None, + third_party_invite: None, + }) + .unwrap() +} + +pub fn to_init_pdu_event( + id: &str, + sender: UserId, + ev_type: EventType, + state_key: Option<&str>, + content: JsonValue, +) -> Arc { + let ts = unsafe { + let ts = SERVER_TIMESTAMP; + // increment the "origin_server_ts" value + SERVER_TIMESTAMP += 1; + ts + }; + let id = if id.contains('$') { + id.to_string() + } else { + format!("${}:foo", id) + }; + + let json = if let Some(state_key) = state_key { + json!({ + "auth_events": [], + "prev_events": [], + "event_id": id, + "sender": sender, + "type": ev_type, + "state_key": state_key, + "content": content, + "origin_server_ts": ts, + "room_id": room_id(), + "origin": "foo", + "depth": 0, + "hashes": { "sha256": "hello" }, + "signatures": {}, + }) + } else { + json!({ + "auth_events": [], + "prev_events": [], + "event_id": id, + "sender": sender, + "type": ev_type, + "content": content, + "origin_server_ts": ts, + "room_id": room_id(), + "origin": "foo", + "depth": 0, + "hashes": { "sha256": "hello" }, + "signatures": {}, + }) + }; + Arc::new(serde_json::from_value(json).unwrap()) +} + +pub fn to_pdu_event( + id: &str, + sender: UserId, + ev_type: EventType, + state_key: Option<&str>, + content: JsonValue, + auth_events: &[S], + prev_events: &[S], +) -> Arc +where + S: AsRef, +{ + let ts = unsafe { + let ts = SERVER_TIMESTAMP; + // increment the "origin_server_ts" value + SERVER_TIMESTAMP += 1; + ts + }; + let id = if id.contains('$') { + id.to_string() + } else { + format!("${}:foo", id) + }; + let auth_events = auth_events + .iter() + .map(AsRef::as_ref) + .map(event_id) + .collect::>(); + let prev_events = prev_events + .iter() + .map(AsRef::as_ref) + .map(event_id) + .collect::>(); + + let json = if let Some(state_key) = state_key { + json!({ + "auth_events": auth_events, + "prev_events": prev_events, + "event_id": id, + "sender": sender, + "type": ev_type, + "state_key": state_key, + "content": content, + "origin_server_ts": ts, + "room_id": room_id(), + "origin": "foo", + "depth": 0, + "hashes": { "sha256": "hello" }, + "signatures": {}, + }) + } else { + json!({ + "auth_events": auth_events, + "prev_events": prev_events, + "event_id": id, + "sender": sender, + "type": ev_type, + "content": content, + "origin_server_ts": ts, + "room_id": room_id(), + "origin": "foo", + "depth": 0, + "hashes": { "sha256": "hello" }, + "signatures": {}, + }) + }; + Arc::new(serde_json::from_value(json).unwrap()) +} + +// all graphs start with these input events +#[allow(non_snake_case)] +pub fn INITIAL_EVENTS() -> BTreeMap> { + // this is always called so we can init the logger here + let _ = LOGGER.call_once(|| { + tracer::fmt() + .with_env_filter(tracer::EnvFilter::from_default_env()) + .init() + }); + + vec![ + to_pdu_event::( + "CREATE", + alice(), + EventType::RoomCreate, + Some(""), + json!({ "creator": alice() }), + &[], + &[], + ), + to_pdu_event( + "IMA", + alice(), + EventType::RoomMember, + Some(alice().to_string().as_str()), + member_content_join(), + &["CREATE"], + &["CREATE"], + ), + to_pdu_event( + "IPOWER", + alice(), + EventType::RoomPowerLevels, + Some(""), + json!({"users": {alice().to_string(): 100}}), + &["CREATE", "IMA"], + &["IMA"], + ), + to_pdu_event( + "IJR", + alice(), + EventType::RoomJoinRules, + Some(""), + json!({ "join_rule": JoinRule::Public }), + &["CREATE", "IMA", "IPOWER"], + &["IPOWER"], + ), + to_pdu_event( + "IMB", + bob(), + EventType::RoomMember, + Some(bob().to_string().as_str()), + member_content_join(), + &["CREATE", "IJR", "IPOWER"], + &["IJR"], + ), + to_pdu_event( + "IMC", + charlie(), + EventType::RoomMember, + Some(charlie().to_string().as_str()), + member_content_join(), + &["CREATE", "IJR", "IPOWER"], + &["IMB"], + ), + to_pdu_event::( + "START", + charlie(), + EventType::RoomTopic, + Some(""), + json!({}), + &[], + &[], + ), + to_pdu_event::( + "END", + charlie(), + EventType::RoomTopic, + Some(""), + json!({}), + &[], + &[], + ), + ] + .into_iter() + .map(|ev| (ev.event_id(), ev)) + .collect() +} + +#[allow(non_snake_case)] +pub fn INITIAL_EDGES() -> Vec { + vec!["START", "IMC", "IMB", "IJR", "IPOWER", "IMA", "CREATE"] + .into_iter() + .map(event_id) + .collect::>() +}