use std::collections::HashSet;
use std::time::Duration;
use p2panda_core::SigningKey;
use rand::SeedableRng;
use rand_chacha::ChaCha20Rng;
use crate::address_book::test_utils::{TestNodeId, TestNodeInfo, current_timestamp};
use crate::address_book::{AddressBookStore, NodeInfo};
use crate::{SqliteStore, Transaction};
#[tokio::test]
async fn insert_node_info() {
let store = SqliteStore::temporary().await;
let permit = store.begin().await.unwrap();
let node_id = SigningKey::generate().verifying_key();
let node_info = TestNodeInfo::new(node_id);
let result = store.insert_node_info(node_info.clone()).await.unwrap();
assert!(result);
store.commit(permit).await.unwrap();
assert_eq!(
store.node_info(&node_info.id).await.unwrap(),
Some(node_info.clone())
);
}
#[tokio::test]
async fn set_and_query_topics() {
let store = SqliteStore::temporary().await;
let billie = SigningKey::generate().verifying_key();
let daphne = SigningKey::generate().verifying_key();
let carlos = SigningKey::generate().verifying_key();
let cats = [100; 32].into();
let dogs = [102; 32].into();
let rain = [104; 32].into();
let frogs = [106; 32].into();
let trains = [200; 32].into();
let permit = store.begin().await.unwrap();
store
.insert_node_info(TestNodeInfo::new(billie))
.await
.unwrap();
<SqliteStore as AddressBookStore<TestNodeId, TestNodeInfo>>::set_topics(
&store,
billie,
HashSet::from_iter([cats, dogs, rain]),
)
.await
.unwrap();
store
.insert_node_info(TestNodeInfo::new(daphne))
.await
.unwrap();
<SqliteStore as AddressBookStore<TestNodeId, TestNodeInfo>>::set_topics(
&store,
daphne,
HashSet::from_iter([rain]),
)
.await
.unwrap();
store
.insert_node_info(TestNodeInfo::new(carlos))
.await
.unwrap();
<SqliteStore as AddressBookStore<TestNodeId, TestNodeInfo>>::set_topics(
&store,
carlos,
HashSet::from_iter([dogs, frogs]),
)
.await
.unwrap();
store.commit(permit).await.unwrap();
assert_eq!(
store
.node_infos_by_topics(&[dogs])
.await
.unwrap()
.into_iter()
.map(|item: TestNodeInfo| item.id)
.collect::<HashSet<TestNodeId>>(),
HashSet::from_iter([billie, carlos])
);
assert_eq!(
store
.node_infos_by_topics(&[frogs, rain])
.await
.unwrap()
.into_iter()
.map(|item: TestNodeInfo| item.id)
.collect::<HashSet<TestNodeId>>(),
HashSet::from_iter([billie, carlos, daphne])
);
assert!(
store
.node_infos_by_topics(&[trains])
.await
.unwrap()
.into_iter()
.map(|item: TestNodeInfo| item.id)
.collect::<Vec<TestNodeId>>()
.is_empty()
);
}
#[tokio::test]
async fn remove_outdated_node_infos() {
let store = SqliteStore::temporary().await;
let billie = SigningKey::generate().verifying_key();
let daphne = SigningKey::generate().verifying_key();
let permit = store.begin().await.unwrap();
store
.insert_node_info(TestNodeInfo::new(billie))
.await
.unwrap();
store
.set_last_changed(&billie, current_timestamp() - (60 * 2))
.await
.unwrap();
store
.insert_node_info(TestNodeInfo::new(daphne))
.await
.unwrap();
store.commit(permit).await.unwrap();
let permit = store.begin().await.unwrap();
let result = <SqliteStore as AddressBookStore<TestNodeId, TestNodeInfo>>::remove_older_than(
&store,
Duration::from_secs(60),
)
.await
.unwrap();
assert_eq!(result, 1);
store.commit(permit).await.unwrap();
assert!(
<SqliteStore as AddressBookStore<TestNodeId, TestNodeInfo>>::node_info(&store, &billie)
.await
.unwrap()
.is_none(),
);
assert!(
<SqliteStore as AddressBookStore<TestNodeId, TestNodeInfo>>::node_info(&store, &daphne)
.await
.unwrap()
.is_some(),
);
}
#[tokio::test]
async fn sample_random_nodes() {
let store = SqliteStore::temporary().await;
let mut rng = ChaCha20Rng::from_seed([1; 32]);
let permit = store.begin().await.unwrap();
for _ in 0..100 {
let id = SigningKey::generate().verifying_key();
store
.insert_node_info(TestNodeInfo::new(id).with_random_address(&mut rng))
.await
.unwrap();
}
for _ in 200..300 {
let id = SigningKey::generate().verifying_key();
store
.insert_node_info(TestNodeInfo::new_bootstrap(id).with_random_address(&mut rng))
.await
.unwrap();
}
store.commit(permit).await.unwrap();
let mut samples = HashSet::new();
for _ in 0..100 {
samples.insert(
<SqliteStore as AddressBookStore<TestNodeId, TestNodeInfo>>::random_node(&store)
.await
.unwrap(),
);
}
assert!(samples.len() > 25);
let mut samples = HashSet::new();
for _ in 0..100 {
let sample: TestNodeInfo = store.random_bootstrap_node().await.unwrap().unwrap();
assert!(sample.is_bootstrap());
samples.insert(sample);
}
assert!(samples.len() > 25);
}