mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-05-25 02:32:11 +00:00
restructure tests
This commit is contained in:
parent
bcb2403059
commit
1cf4239149
3 changed files with 232 additions and 205 deletions
|
|
@ -1,5 +1,6 @@
|
|||
pub mod error;
|
||||
pub mod stores;
|
||||
#[cfg(test)]
|
||||
pub mod tests;
|
||||
pub mod traits;
|
||||
|
||||
|
|
@ -256,6 +257,7 @@ impl<Store: UserDiscoveryStore, Utils: UserDiscoveryUtils> UserDiscovery<Store,
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) async fn get_contact_version(&self, contact_id: UserID) -> Result<Option<Vec<u8>>> {
|
||||
self.store.get_contact_version(contact_id).await
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<u8> {
|
||||
UserDiscoveryVersion {
|
||||
announcement,
|
||||
promotion,
|
||||
struct TestUsers<S: UserDiscoveryStore> {
|
||||
names: Vec<&'static str>,
|
||||
friends: Vec<Vec<usize>>,
|
||||
message_flows: Vec<(usize, usize)>,
|
||||
announced_users_expected: Vec<Vec<(usize, Vec<usize>)>>,
|
||||
uds: Vec<UserDiscovery<S, TestingUtils>>,
|
||||
}
|
||||
|
||||
async fn get_with_five_users<S: UserDiscoveryStore + Default + Clone>() -> TestUsers<S> {
|
||||
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::<InMemoryStore>(&users).await;
|
||||
step1_verify_no_new_messages::<InMemoryStore>(&users).await;
|
||||
step2_verify_announced_users_expected::<InMemoryStore>(&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::<InMemoryStore>(&users).await;
|
||||
step1_verify_no_new_messages::<InMemoryStore>(&users).await;
|
||||
step2_verify_announced_users_expected::<InMemoryStore>(&users).await;
|
||||
}
|
||||
|
||||
async fn step0_exchange_in_order<S: UserDiscoveryStore + Clone + Default>(users: &TestUsers<S>) {
|
||||
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<S: UserDiscoveryStore + Clone + Default>(users: &TestUsers<S>) {
|
||||
let mut user_ids: Vec<usize> = (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<S: UserDiscoveryStore + Clone + Default>(
|
||||
users: &TestUsers<S>,
|
||||
) {
|
||||
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<S: UserDiscoveryStore + Clone + Default>(
|
||||
users: &TestUsers<S>,
|
||||
) {
|
||||
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::<Vec<_>>());
|
||||
}
|
||||
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::<Vec<_>>()
|
||||
.join(", ")
|
||||
);
|
||||
let announced_users: HashSet<i64> = announced_users.iter().cloned().collect();
|
||||
let announced_users_expected: HashSet<i64> = 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<S: UserDiscoveryStore + Clone + Default>(
|
||||
user_id: usize,
|
||||
threshold: u8,
|
||||
store: S,
|
||||
) -> UserDiscovery<S, TestingUtils> {
|
||||
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<S: UserDiscoveryStore>(
|
|||
async fn request_and_handle_messages<S: UserDiscoveryStore>(
|
||||
from: (usize, &UserDiscovery<S, TestingUtils>),
|
||||
to: (usize, &UserDiscovery<S, TestingUtils>),
|
||||
messages_count: usize,
|
||||
messages_count: Option<usize>,
|
||||
) {
|
||||
// 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<S: UserDiscoveryStore>(
|
|||
.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<S: UserDiscoveryStore> {
|
||||
names: [&'static str; TEST_USER_COUNT],
|
||||
friends: [Vec<usize>; TEST_USER_COUNT],
|
||||
uds: Vec<UserDiscovery<S, TestingUtils>>,
|
||||
}
|
||||
async fn to_all_friends<S: UserDiscoveryStore + Clone>(
|
||||
from: usize,
|
||||
message_count: usize,
|
||||
users: &TestUsers<S>,
|
||||
) {
|
||||
for friend in &users.friends[from] {
|
||||
tracing::debug!("From {} to {}", users.names[from], users.names[*friend]);
|
||||
|
||||
impl<S: UserDiscoveryStore + Clone + Default> TestUsers<S> {
|
||||
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<S: UserDiscoveryStore + Clone + Default>() {
|
||||
#[cfg(test)]
|
||||
let _ = pretty_env_logger::try_init();
|
||||
|
||||
let users = TestUsers::<S>::get().await;
|
||||
|
||||
async fn to_all_friends<S: UserDiscoveryStore + Clone>(
|
||||
from: usize,
|
||||
message_count: usize,
|
||||
users: &TestUsers<S>,
|
||||
) {
|
||||
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::<Vec<_>>());
|
||||
}
|
||||
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::<Vec<_>>()
|
||||
.join(", ")
|
||||
);
|
||||
let announced_users: HashSet<i64> = announced_users.iter().cloned().collect();
|
||||
let announced_users_expected: HashSet<i64> = 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<u8> {
|
||||
UserDiscoveryVersion {
|
||||
announcement,
|
||||
promotion,
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_initialize_user_discovery_in_memory_store() {
|
||||
test_initialize_user_discovery::<crate::user_discovery::stores::InMemoryStore>().await;
|
||||
.encode_to_vec()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ pub trait UserDiscoveryUtils {
|
|||
) -> impl Future<Output = Result<bool>> + Send;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use crate::user_discovery::traits::UserDiscoveryUtils;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue