diff --git a/rust_dependencies/protocols/src/user_discovery.rs b/rust_dependencies/protocols/src/user_discovery.rs index cb8fd75b..0b7a8d8d 100644 --- a/rust_dependencies/protocols/src/user_discovery.rs +++ b/rust_dependencies/protocols/src/user_discovery.rs @@ -1,5 +1,6 @@ pub mod error; pub mod stores; +#[cfg(test)] pub mod tests; pub mod traits; @@ -256,6 +257,7 @@ impl UserDiscovery Result>> { self.store.get_contact_version(contact_id).await } diff --git a/rust_dependencies/protocols/src/user_discovery/tests.rs b/rust_dependencies/protocols/src/user_discovery/tests.rs index acabc265..63d200da 100644 --- a/rust_dependencies/protocols/src/user_discovery/tests.rs +++ b/rust_dependencies/protocols/src/user_discovery/tests.rs @@ -1,24 +1,193 @@ +use crate::user_discovery::stores::InMemoryStore; use crate::user_discovery::traits::tests::TestingUtils; use crate::user_discovery::{UserDiscovery, UserDiscoveryStore, UserDiscoveryVersion, UserID}; use prost::Message; +use rand::seq::SliceRandom; use std::collections::{HashMap, HashSet}; use std::vec; -fn get_version_bytes(announcement: u32, promotion: u32) -> Vec { - UserDiscoveryVersion { - announcement, - promotion, +struct TestUsers { + names: Vec<&'static str>, + friends: Vec>, + message_flows: Vec<(usize, usize)>, + announced_users_expected: Vec)>>, + uds: Vec>, +} + +async fn get_with_five_users() -> TestUsers { + const ALICE: usize = 0; + const BOB: usize = 1; + const CHARLIE: usize = 2; + const DAVID: usize = 3; + const FRANK: usize = 4; + + let names = vec!["ALICE", "BOB", "CHARLIE", "DAVID", "FRANK"]; + let mut uds = vec![]; + + for index in 0..names.len() { + let store = S::default(); + uds.push(get_ud(index, 2, store).await); + } + + TestUsers { + names, + uds, + friends: vec![ + vec![BOB, CHARLIE], + vec![ALICE, CHARLIE, DAVID], + vec![ALICE, BOB, DAVID, FRANK], + vec![BOB, CHARLIE], + vec![CHARLIE], + ], + message_flows: vec![ + (ALICE, 1), // ALICE: own announcement sending to BOB and CHARLIE + (BOB, 2), // BOB: own announcement + promotion for ALICE + (BOB, 0), // BOBs version should not have any new messages for his friends + (ALICE, 1), // ALICE: promotion for BOB + (CHARLIE, 3), // CHARLIE: own announcement + promotion for ALICE, BOB + (DAVID, 3), // DAVID: own announcement + promotion for BOB, CHARLIE + (BOB, 2), // BOB: promotion for CHARLIE, DAVID + (CHARLIE, 1), // CHARLIE: promotion for DAVID + (FRANK, 2), // FRANK: own announcement + promotion for CHARLIE + (CHARLIE, 1), // CHARLIE: promotion for FRANK + (ALICE, 1), // ALICE: promotion for CHARLIE + ], + announced_users_expected: vec![ + // ALICE should now know that BOB and CHARLIE, BOB and DAVID and CHARLIE and DAVID are friends. + // Alice should also have one protected share from Frank. + vec![ + (BOB, vec![CHARLIE]), // ALICE knows Bob and that CHARLIE is connected with BOB + (CHARLIE, vec![BOB]), // ALICE knows CHARLIE and that BOB is connected with CHARLIE + (DAVID, vec![BOB, CHARLIE]), // ALICE knows DAVID and that BOB and CHARLIE are connected with DAVID + ], + vec![ + (ALICE, vec![CHARLIE]), + (CHARLIE, vec![ALICE, DAVID]), + (DAVID, vec![CHARLIE]), + ], + vec![ + (ALICE, vec![BOB]), + (BOB, vec![ALICE, DAVID]), + (DAVID, vec![BOB]), + (FRANK, vec![]), + ], + vec![ + (ALICE, vec![BOB, CHARLIE]), + (BOB, vec![CHARLIE]), + (CHARLIE, vec![BOB]), + ], + vec![(CHARLIE, vec![])], + ], + } +} + +#[tokio::test] +async fn test_user_discovery_in_memory_store() { + let _ = pretty_env_logger::try_init(); + + let users = get_with_five_users().await; + step0_exchange_in_order::(&users).await; + step1_verify_no_new_messages::(&users).await; + step2_verify_announced_users_expected::(&users).await; +} + +#[tokio::test] +async fn test_user_discovery_random_order_in_memory_store() { + let _ = pretty_env_logger::try_init(); + + let users = get_with_five_users().await; + step0_exchange_random::(&users).await; + step1_verify_no_new_messages::(&users).await; + step2_verify_announced_users_expected::(&users).await; +} + +async fn step0_exchange_in_order(users: &TestUsers) { + for (i, (from, count)) in users.message_flows.iter().enumerate() { + tracing::debug!("MESSAGE FLOW: {i}"); + to_all_friends(*from, *count, &users).await; + } +} + +async fn step0_exchange_random(users: &TestUsers) { + let mut user_ids: Vec = (0..users.names.len()).collect(); + + for _ in 0..100 { + user_ids.shuffle(&mut rand::rng()); + + for from in user_ids.clone() { + for friend in &users.friends[from] { + request_and_handle_messages( + (from, &users.uds[from]), + (*friend, &users.uds[*friend]), + None, + ) + .await; + } + } + } +} + +async fn step1_verify_no_new_messages( + users: &TestUsers, +) { + tracing::debug!("Now all users should have the newest version."); + + for from in 0..users.names.len() { + for to in &users.friends[from] { + tracing::debug!( + "Does {} has open messages for {}?", + &users.names[from], + &users.names[*to] + ); + assert_new_messages((from, &users.uds[from]), (*to, &users.uds[*to]), false).await; + } + } +} + +async fn step2_verify_announced_users_expected( + users: &TestUsers, +) { + tracing::debug!("Test if all exchanges where successful."); + + for (user, announcements) in users.announced_users_expected.iter().enumerate() { + let announced_users2 = users.uds[user].get_all_announced_users().await.unwrap(); + let mut announced_users = HashMap::new(); + for a in announced_users2 { + announced_users.insert(a.0.user_id, a.1.iter().map(|x| x.0).collect::>()); + } + tracing::debug!("{} knows now: {}", users.names[user], announced_users.len()); + assert_eq!(announced_users.len(), announcements.len()); + for (contact_id, announced_users_expected) in announcements { + let announced_users = announced_users.get(&(*contact_id as i64)).unwrap(); + tracing::debug!( + "{} knows now that {} has the following friends: {}", + users.names[user], + users.names[*contact_id], + announced_users + .iter() + .map(|x| users.names[*x as usize]) + .collect::>() + .join(", ") + ); + let announced_users: HashSet = announced_users.iter().cloned().collect(); + let announced_users_expected: HashSet = announced_users_expected + .iter() + .cloned() + .map(|x| x as i64) + .collect(); + assert_eq!(announced_users, announced_users_expected); + } } - .encode_to_vec() } async fn get_ud( user_id: usize, + threshold: u8, + store: S, ) -> UserDiscovery { - let store = S::default(); let ud = UserDiscovery::new(store.to_owned(), TestingUtils::default()).unwrap(); - ud.initialize_or_update(2, user_id as UserID, vec![user_id as u8; 32]) + ud.initialize_or_update(threshold, user_id as UserID, vec![user_id as u8; 32]) .await .unwrap(); @@ -47,17 +216,20 @@ async fn assert_new_messages( async fn request_and_handle_messages( from: (usize, &UserDiscovery), to: (usize, &UserDiscovery), - messages_count: usize, + messages_count: Option, ) { // From sends a message with his current version to To - let to_received_version = &from.1.get_current_version().await.unwrap(); - assert_eq!( - to.1.should_request_new_messages(from.0 as UserID, to_received_version) - .await - .unwrap() - .is_some(), - true - ); + + if messages_count.is_some() { + let to_received_version = &from.1.get_current_version().await.unwrap(); + assert_eq!( + to.1.should_request_new_messages(from.0 as UserID, to_received_version) + .await + .unwrap() + .is_some(), + true + ); + } // As To has a older version stored he sends a request to From: Give me all messages since version. let from_request_version_from_to = @@ -72,206 +244,58 @@ async fn request_and_handle_messages( .await .unwrap(); - assert!(new_messages.len() <= messages_count); + if let Some(messages_count) = messages_count { + assert!(new_messages.len() <= messages_count); + } to.1.handle_new_messages(from.0 as UserID, new_messages) .await .unwrap(); - assert_eq!( - to.1.should_request_new_messages( - from.0 as UserID, - &from.1.get_current_version().await.unwrap() - ) - .await - .unwrap() - .is_some(), - false - ); + if messages_count.is_some() { + assert_eq!( + to.1.should_request_new_messages( + from.0 as UserID, + &from.1.get_current_version().await.unwrap() + ) + .await + .unwrap() + .is_some(), + false + ); + } } -const ALICE: usize = 0; -const BOB: usize = 1; -const CHARLIE: usize = 2; -const DAVID: usize = 3; -const FRANK: usize = 4; -const TEST_USER_COUNT: usize = 5; -struct TestUsers { - names: [&'static str; TEST_USER_COUNT], - friends: [Vec; TEST_USER_COUNT], - uds: Vec>, -} +async fn to_all_friends( + from: usize, + message_count: usize, + users: &TestUsers, +) { + for friend in &users.friends[from] { + tracing::debug!("From {} to {}", users.names[from], users.names[*friend]); -impl TestUsers { - async fn get() -> Self { - let names = ["ALICE", "BOB", "CHARLIE", "DAVID", "FRANK"]; - let mut uds = vec![]; - for index in 0..names.len() { - uds.push(get_ud(index).await); - } - let friends = [ - vec![BOB, CHARLIE], - vec![ALICE, CHARLIE, DAVID], - vec![ALICE, BOB, DAVID, FRANK], - vec![BOB, CHARLIE], - vec![CHARLIE], - ]; - Self { - names, - uds, - friends, + if message_count == 0 { + assert_new_messages( + (from, &users.uds[from]), + (*friend, &users.uds[*friend]), + false, + ) + .await; + } else { + request_and_handle_messages( + (from, &users.uds[from]), + (*friend, &users.uds[*friend]), + Some(message_count), + ) + .await; } } } -pub async fn test_initialize_user_discovery() { - #[cfg(test)] - let _ = pretty_env_logger::try_init(); - - let users = TestUsers::::get().await; - - async fn to_all_friends( - from: usize, - message_count: usize, - users: &TestUsers, - ) { - for friend in &users.friends[from] { - tracing::debug!("From {} to {}", users.names[from], users.names[*friend]); - - if message_count == 0 { - assert_new_messages( - (from, &users.uds[from]), - (*friend, &users.uds[*friend]), - false, - ) - .await; - } else { - request_and_handle_messages( - (from, &users.uds[from]), - (*friend, &users.uds[*friend]), - message_count, - ) - .await; - } - } - } - - let message_flows = [ - // ALICE: own announcement sending to BOB and CHARLIE - (ALICE, 1), - // BOB: own announcement + promotion for ALICE - (BOB, 2), - // BOBs version should not have any new messages for his friends - (BOB, 0), - // ALICE: promotion for BOB - (ALICE, 1), - // CHARLIE: own announcement + promotion for ALICE, BOB - (CHARLIE, 3), - // DAVID: own announcement + promotion for BOB, CHARLIE - (DAVID, 3), - // BOB: promotion for CHARLIE, DAVID - (BOB, 2), - // CHARLIE: promotion for DAVID - (CHARLIE, 1), - // FRANK: own announcement + promotion for CHARLIE - (FRANK, 2), - // CHARLIE: promotion for FRANK - (CHARLIE, 1), - // ALICE: promotion for CHARLIE - (ALICE, 1), - ]; - - for (i, (from, count)) in message_flows.into_iter().enumerate() { - tracing::debug!("MESSAGE FLOW: {i}"); - to_all_friends(from, count, &users).await; - } - - tracing::debug!("Now all users should have the newest version."); - - for from in 0..TEST_USER_COUNT { - for to in &users.friends[from] { - tracing::debug!( - "Does {} has open messages for {}?", - &users.names[from], - &users.names[*to] - ); - assert_new_messages((from, &users.uds[from]), (*to, &users.uds[*to]), false).await; - } - } - - tracing::debug!("Test if all exchanges where successful."); - - let announced_users_expected = [ - // ALICE should now know that BOB and CHARLIE, BOB and DAVID and CHARLIE and DAVID are friends. - // Alice should also have one protected share from Frank. - ( - ALICE, - vec![ - (BOB, vec![CHARLIE]), // ALICE knows Bob and that CHARLIE is connected with BOB - (CHARLIE, vec![BOB]), // ALICE knows CHARLIE and that BOB is connected with CHARLIE - (DAVID, vec![BOB, CHARLIE]), // ALICE knows DAVID and that BOB and CHARLIE are connected with DAVID - ], - ), - ( - BOB, - vec![ - (ALICE, vec![CHARLIE]), - (CHARLIE, vec![ALICE, DAVID]), - (DAVID, vec![CHARLIE]), - ], - ), - ( - CHARLIE, - vec![ - (ALICE, vec![BOB]), - (BOB, vec![ALICE, DAVID]), - (DAVID, vec![BOB]), - (FRANK, vec![]), - ], - ), - ( - DAVID, - vec![ - (ALICE, vec![BOB, CHARLIE]), - (BOB, vec![CHARLIE]), - (CHARLIE, vec![BOB]), - ], - ), - (FRANK, vec![(CHARLIE, vec![])]), - ]; - - for (user, announcements) in announced_users_expected { - let announced_users2 = users.uds[user].get_all_announced_users().await.unwrap(); - let mut announced_users = HashMap::new(); - for a in announced_users2 { - announced_users.insert(a.0.user_id, a.1.iter().map(|x| x.0).collect::>()); - } - tracing::debug!("{} knows now: {}", users.names[user], announced_users.len()); - assert_eq!(announced_users.len(), announcements.len()); - for (contact_id, announced_users_expected) in announcements { - let announced_users = announced_users.get(&(contact_id as i64)).unwrap(); - tracing::debug!( - "{} knows now that {} has the following friends: {}", - users.names[user], - users.names[contact_id], - announced_users - .iter() - .map(|x| users.names[*x as usize]) - .collect::>() - .join(", ") - ); - let announced_users: HashSet = announced_users.iter().cloned().collect(); - let announced_users_expected: HashSet = announced_users_expected - .iter() - .cloned() - .map(|x| x as i64) - .collect(); - assert_eq!(announced_users, announced_users_expected); - } +fn get_version_bytes(announcement: u32, promotion: u32) -> Vec { + UserDiscoveryVersion { + announcement, + promotion, } -} - -#[tokio::test] -async fn test_initialize_user_discovery_in_memory_store() { - test_initialize_user_discovery::().await; + .encode_to_vec() } diff --git a/rust_dependencies/protocols/src/user_discovery/traits.rs b/rust_dependencies/protocols/src/user_discovery/traits.rs index 8c4c51d3..7b5c013c 100644 --- a/rust_dependencies/protocols/src/user_discovery/traits.rs +++ b/rust_dependencies/protocols/src/user_discovery/traits.rs @@ -97,6 +97,7 @@ pub trait UserDiscoveryUtils { ) -> impl Future> + Send; } +#[cfg(test)] pub(crate) mod tests { use crate::user_discovery::traits::UserDiscoveryUtils;