#![allow(dead_code)]
use crate::{
api::api_types::*,
dsnp::{dsnp_configs::DsnpVersionConfig, dsnp_types::*},
graph::{
key_manager::{UserKeyManagerBase, USER_KEY_MANAGER},
page::{PrivatePageDataProvider, PublicPageDataProvider, RemovedPageDataProvider},
page_capacities::PAGE_CAPACITY_MAP,
updates::UpdateEvent,
},
util::{
time::duration_days_since,
transactional_hashmap::{Transactional, TransactionalHashMap},
},
};
use dsnp_graph_config::{
errors::{DsnpGraphError, DsnpGraphResult},
Environment, SchemaId,
};
use log::Level;
use log_result_proc_macro::log_result_err;
use std::{
collections::{BTreeMap, HashMap, HashSet},
iter::Peekable,
sync::{Arc, RwLock},
};
use super::page::GraphPage;
pub type PageMap = TransactionalHashMap<PageId, GraphPage>;
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum PageFullnessMode {
Trivial,
Aggressive,
}
#[derive(Debug, Clone)]
pub struct Graph {
environment: Environment,
user_id: DsnpUserId,
schema_id: SchemaId,
pages: PageMap,
user_key_manager: Arc<RwLock<dyn UserKeyManagerBase + 'static + Send + Sync>>,
}
impl PartialEq for Graph {
fn eq(&self, other: &Self) -> bool {
self.environment == other.environment &&
self.user_id == other.user_id &&
self.schema_id == other.schema_id &&
self.pages.eq(&other.pages)
}
}
impl Transactional for Graph {
fn commit(&mut self) {
let page_ids: Vec<_> = self.pages.inner().keys().copied().collect();
for pid in page_ids {
if let Some(g) = self.pages.get_mut(&pid) {
g.commit();
}
}
self.pages.commit();
}
fn rollback(&mut self) {
self.pages.rollback();
let page_ids: Vec<_> = self.pages.inner().keys().copied().collect();
for pid in page_ids {
if let Some(g) = self.pages.get_mut(&pid) {
g.rollback();
}
}
}
}
impl Graph {
pub fn new<E>(
environment: Environment,
user_id: DsnpUserId,
schema_id: SchemaId,
user_key_manager: Arc<RwLock<E>>,
) -> Self
where
E: UserKeyManagerBase + 'static + Send + Sync,
{
Self { environment, user_id, schema_id, pages: PageMap::new(), user_key_manager }
}
pub fn len(&self) -> usize {
self.pages.inner().values().flat_map(|p| p.connections()).count()
}
pub fn pages(&self) -> &PageMap {
&self.pages
}
#[cfg(test)]
pub fn set_pages(&mut self, pages: PageMap) {
self.pages = pages;
}
#[cfg(test)]
pub fn get_user_key_mgr(&self) -> Arc<RwLock<dyn UserKeyManagerBase + 'static + Send + Sync>> {
self.user_key_manager.clone()
}
pub fn get_next_available_page_id(
&self,
updated_pages: &BTreeMap<PageId, GraphPage>,
) -> Option<PageId> {
let existing_pages = self
.pages
.inner()
.keys()
.cloned()
.chain(updated_pages.keys().cloned())
.collect::<HashSet<PageId>>();
(0..=(self.environment.get_config().max_page_id as PageId))
.find(|&pid| !existing_pages.contains(&pid))
}
pub fn clear(&mut self) {
self.pages.clear();
}
pub fn get_connection_type(&self) -> ConnectionType {
self.environment
.get_config()
.get_connection_type_from_schema_id(self.schema_id)
.expect("Connection type should exist!")
}
pub fn get_schema_id(&self) -> SchemaId {
self.schema_id
}
pub fn get_dsnp_user_id(&self) -> DsnpUserId {
self.user_id
}
#[log_result_err(Level::Info)]
pub fn import_public(
&mut self,
connection_type: ConnectionType,
pages: &Vec<PageData>,
) -> DsnpGraphResult<()> {
if connection_type != self.get_connection_type() {
return Err(DsnpGraphError::IncorrectConnectionType(format!(
"Expected {:?} but got {:?}",
self.get_connection_type(),
connection_type
)))
}
let max_page_id = self.environment.get_config().max_page_id;
let mut page_map = HashMap::new();
for page in pages.iter() {
if page.page_id > max_page_id as PageId {
return Err(DsnpGraphError::InvalidPageId(page.page_id))
}
match GraphPage::try_from(page) {
Err(e) => return Err(DsnpGraphError::from(e)),
Ok(p) => {
page_map.insert(page.page_id, p);
},
};
}
self.pages.clear();
for (page_id, page) in page_map {
self.pages.insert(page_id, page);
}
Ok(())
}
#[log_result_err(Level::Info)]
pub fn import_private(
&mut self,
dsnp_version_config: &DsnpVersionConfig,
connection_type: ConnectionType,
pages: &[PageData],
) -> DsnpGraphResult<()> {
if connection_type != self.get_connection_type() {
return Err(DsnpGraphError::IncorrectConnectionType(format!(
"Expected {:?} but got {:?}",
self.get_connection_type(),
connection_type
)))
}
let max_page_id = self.environment.get_config().max_page_id;
let keys = self
.user_key_manager
.read()
.map_err(|_| DsnpGraphError::FailedtoReadLock(USER_KEY_MANAGER.to_string()))?
.get_all_resolved_keys();
let mut page_map = HashMap::new();
for page in pages.iter() {
if page.page_id > max_page_id as PageId {
return Err(DsnpGraphError::InvalidPageId(page.page_id))
}
match GraphPage::try_from((page, dsnp_version_config, &keys)) {
Err(e) => return Err(DsnpGraphError::from(e)),
Ok(p) => {
p.verify_prid_len(self.get_connection_type())?;
page_map.insert(page.page_id, p);
},
};
}
self.pages.clear();
for (page_id, page) in page_map {
self.pages.insert(page_id, page);
}
Ok(())
}
#[log_result_err(Level::Info)]
pub fn calculate_updates(
&self,
dsnp_version_config: &DsnpVersionConfig,
updates: &Vec<UpdateEvent>,
) -> DsnpGraphResult<Vec<Update>> {
let encryption_key = match self.get_connection_type().privacy_type() {
PrivacyType::Public => None,
PrivacyType::Private => self
.user_key_manager
.read()
.map_err(|_| DsnpGraphError::FailedtoReadLock(USER_KEY_MANAGER.to_string()))?
.get_resolved_active_key(self.user_id),
};
let ids_to_remove: Vec<DsnpUserId> = updates
.iter()
.filter_map(|event| match event {
UpdateEvent::Remove { dsnp_user_id, .. } => Some(*dsnp_user_id),
_ => None,
})
.collect();
let mut ids_to_add: Vec<DsnpUserId> = updates
.iter()
.filter_map(|event| match event {
UpdateEvent::Add { dsnp_user_id, .. } => Some(*dsnp_user_id),
_ => None,
})
.collect();
ids_to_add.sort();
let pages_with_removals = self.find_connections(&ids_to_remove);
let mut updated_pages: BTreeMap<PageId, GraphPage> = self
.pages
.inner()
.iter()
.filter_map(|(page_id, page)| {
if pages_with_removals.contains(page_id) {
let mut updated_page = page.clone();
updated_page.remove_connections(&ids_to_remove);
return Some((*page_id, updated_page))
}
None
})
.collect();
let mut add_iter = ids_to_add.iter().cloned().peekable();
'fullness_mode_loop: for aggressive in
vec![PageFullnessMode::Trivial, PageFullnessMode::Aggressive]
{
for page in updated_pages.values_mut() {
self.add_to_page_until_full(
page,
&mut add_iter,
aggressive,
dsnp_version_config,
&encryption_key,
);
if let None = add_iter.peek() {
break 'fullness_mode_loop
}
}
}
let mut remaining_pages: Vec<&GraphPage> =
self.pages
.inner()
.iter()
.filter_map(|(page_id, page)| {
if !updated_pages.keys().any(|k| k == page_id) {
Some(page)
} else {
None
}
})
.collect();
remaining_pages.sort_by_key(|page| page.connections().len());
for page in remaining_pages {
let mut current_page = page.clone();
let page_modified = self.add_to_page_until_full(
&mut current_page,
&mut add_iter,
PageFullnessMode::Aggressive,
dsnp_version_config,
&encryption_key,
);
if page_modified {
updated_pages.insert(current_page.page_id(), current_page);
}
if let None = add_iter.peek() {
break
}
}
while let Some(_) = add_iter.peek() {
let mut new_page = match self.get_next_available_page_id(&updated_pages) {
Some(next_page_id) =>
Ok(GraphPage::new(self.get_connection_type().privacy_type(), next_page_id)),
None => Err(DsnpGraphError::GraphIsFull),
}?;
if self.add_to_page_until_full(
&mut new_page,
&mut add_iter,
PageFullnessMode::Aggressive,
dsnp_version_config,
&encryption_key,
) {
updated_pages.insert(new_page.page_id(), new_page);
}
}
self.pages_to_updates(&mut updated_pages, encryption_key, dsnp_version_config, &ids_to_add)
}
fn add_to_page_until_full(
&self,
page: &mut GraphPage,
add_iter: &mut Peekable<impl Iterator<Item = u64>>,
fullness_mode: PageFullnessMode,
dsnp_version_config: &DsnpVersionConfig,
encryption_key: &Option<ResolvedKeyPair>,
) -> bool {
let mut page_modified = false;
while let Some(id_to_add) = add_iter.peek() {
if let Ok(_) = self.try_add_connection_to_page(
page,
id_to_add,
fullness_mode,
dsnp_version_config,
encryption_key,
) {
page_modified = true;
let _ = add_iter.next(); } else {
break
}
}
page_modified
}
#[log_result_err(Level::Info)]
fn pages_to_updates(
&self,
updated_pages: &mut BTreeMap<PageId, GraphPage>,
encryption_key: Option<ResolvedKeyPair>,
dsnp_version_config: &DsnpVersionConfig,
ids_to_add: &Vec<DsnpUserId>,
) -> DsnpGraphResult<Vec<Update>> {
let mut removed_pages: Vec<PageData> = Vec::new();
updated_pages.retain(|_, page| {
if page.is_empty() {
removed_pages.push(page.to_removed_page_data());
return false
}
true
});
let updated_blobs: DsnpGraphResult<Vec<PageData>> = match self.get_connection_type() {
ConnectionType::Follow(PrivacyType::Public) |
ConnectionType::Friendship(PrivacyType::Public) =>
updated_pages.values().map(|page| page.to_public_page_data()).collect(),
ConnectionType::Follow(PrivacyType::Private) => {
let encryption_key =
encryption_key.ok_or(DsnpGraphError::NoResolvedActiveKeyFound)?;
updated_pages
.iter_mut()
.map(|(_, page)| {
page.clear_prids();
page.to_private_page_data(dsnp_version_config, &encryption_key)
})
.collect()
},
ConnectionType::Friendship(PrivacyType::Private) => {
let encryption_key =
encryption_key.ok_or(DsnpGraphError::NoResolvedActiveKeyFound)?;
updated_pages
.iter_mut()
.map(|(_, page)| {
let mut updated_page = page.clone();
self.apply_prids(&mut updated_page, &ids_to_add, &encryption_key)?;
updated_page.to_private_page_data(dsnp_version_config, &encryption_key)
})
.collect()
},
};
let updates: Vec<Update> = updated_blobs?
.into_iter()
.chain(removed_pages.into_iter())
.map(|page_data| Update::from((page_data, self.user_id, self.schema_id)))
.collect();
Ok(updates)
}
#[log_result_err(Level::Info)]
pub fn force_recalculate(
&self,
dsnp_version_config: &DsnpVersionConfig,
) -> DsnpGraphResult<Vec<Update>> {
let encryption_key = match self.get_connection_type().privacy_type() {
PrivacyType::Public => None,
PrivacyType::Private => self
.user_key_manager
.read()
.map_err(|_| DsnpGraphError::FailedtoReadLock(USER_KEY_MANAGER.to_string()))?
.get_resolved_active_key(self.user_id),
};
let mut updates = vec![];
for (_, page) in self.pages.inner() {
let page_data_result = match page.is_empty() {
true => Ok(page.to_removed_page_data()),
false => match self.get_connection_type() {
ConnectionType::Follow(PrivacyType::Public) |
ConnectionType::Friendship(PrivacyType::Public) => page.to_public_page_data(),
ConnectionType::Follow(PrivacyType::Private) => {
let encryption_key = encryption_key
.clone()
.ok_or(DsnpGraphError::NoResolvedActiveKeyFound)?;
let mut updated_page = page.clone();
updated_page.clear_prids();
updated_page.to_private_page_data(dsnp_version_config, &encryption_key)
},
ConnectionType::Friendship(PrivacyType::Private) => {
let encryption_key = encryption_key
.clone()
.ok_or(DsnpGraphError::NoResolvedActiveKeyFound)?;
let mut updated_page = page.clone();
self.apply_prids(&mut updated_page, &vec![], &encryption_key)?;
updated_page.to_private_page_data(dsnp_version_config, &encryption_key)
},
},
};
updates.push(page_data_result?);
}
let mapped = updates
.into_iter()
.map(|page_data| Update::from((page_data, self.user_id, self.schema_id)))
.collect();
Ok(mapped)
}
#[cfg(test)]
#[log_result_err(Level::Error)]
pub fn create_page(
&mut self,
page_id: &PageId,
page: Option<GraphPage>,
) -> DsnpGraphResult<&mut GraphPage> {
if let Some(_existing_page) = self.pages.get(page_id) {
return Err(DsnpGraphError::NewPageForExistingPageId)
}
self.pages.insert(
*page_id,
match page {
Some(page) => page,
None => GraphPage::new(self.get_connection_type().privacy_type(), *page_id),
},
);
match self.get_page_mut(page_id) {
Some(page) => Ok(page),
None => Err(DsnpGraphError::FailedToRetrieveGraphPage),
}
}
pub fn get_page(&self, page_id: &PageId) -> Option<&GraphPage> {
self.pages.get(page_id)
}
pub fn get_page_mut(&mut self, page_id: &PageId) -> Option<&mut GraphPage> {
self.pages.get_mut(page_id)
}
pub fn has_connection(&self, dsnp_id: &DsnpUserId) -> bool {
self.pages.inner().iter().any(|(_, page)| page.contains(dsnp_id))
}
pub fn find_connection(&self, dsnp_id: &DsnpUserId) -> Option<PageId> {
for (id, page) in self.pages.inner().iter() {
if page.contains(dsnp_id) {
return Some(*id)
}
}
None
}
pub fn find_connections(&self, ids: &Vec<DsnpUserId>) -> Vec<PageId> {
self.pages
.inner()
.iter()
.filter_map(|(page_id, page)| match page.contains_any(ids) {
true => Some(*page_id),
false => None,
})
.collect()
}
#[log_result_err(Level::Info)]
pub fn add_connection_to_page(
&mut self,
page_id: &PageId,
connection_id: &DsnpUserId,
) -> DsnpGraphResult<()> {
if self.find_connection(connection_id).is_some() {
return Err(DsnpGraphError::DuplicateConnectionDetected)
}
if !self.pages.inner().contains_key(page_id) {
self.pages.insert(
*page_id,
GraphPage::new(self.get_connection_type().privacy_type(), *page_id),
);
}
match self.get_page_mut(page_id) {
Some(page) => page.add_connection(connection_id),
None => Err(DsnpGraphError::FailedToRetrieveGraphPage),
}
}
#[log_result_err(Level::Info)]
pub fn remove_connection(
&mut self,
connection_id: &DsnpUserId,
) -> DsnpGraphResult<Option<PageId>> {
if let Some(page_id) = self.find_connection(connection_id) {
return match self.get_page_mut(&page_id) {
Some(page) => match page.remove_connection(connection_id) {
Ok(()) => Ok(Some(page_id)),
Err(e) => Err(e),
},
None => Err(DsnpGraphError::FailedToRetrieveGraphPage),
}
}
Ok(None)
}
#[log_result_err(Level::Info)]
pub fn get_one_sided_friendships(&self) -> DsnpGraphResult<Vec<DsnpGraphEdge>> {
if self.get_connection_type() != ConnectionType::Friendship(PrivacyType::Private) {
return Err(DsnpGraphError::CallToPrivateFriendsInPublicGraph)
}
let mut result = vec![];
for c in self.pages.inner().values().flat_map(|g| g.connections()) {
if !self
.user_key_manager
.read()
.map_err(|_| DsnpGraphError::FailedtoReadLock(USER_KEY_MANAGER.to_string()))?
.verify_connection(c.user_id)?
{
result.push(*c)
}
}
Ok(result)
}
#[log_result_err(Level::Info)]
fn apply_prids(
&self,
updated_page: &mut GraphPage,
ids_to_add: &Vec<DsnpUserId>,
encryption_key: &ResolvedKeyPair,
) -> DsnpGraphResult<()> {
if self.get_connection_type() != ConnectionType::Friendship(PrivacyType::Private) {
return Err(DsnpGraphError::CallToPridsInPublicGraph)
}
let max_allowed_stale_days =
self.environment.get_config().sdk_max_stale_friendship_days as u64;
for c in updated_page
.connections()
.clone()
.iter()
.filter(|c| !ids_to_add.contains(&c.user_id))
{
if duration_days_since(c.since) > max_allowed_stale_days &&
!self
.user_key_manager
.read()
.map_err(|_| DsnpGraphError::FailedtoReadLock(USER_KEY_MANAGER.to_string()))?
.verify_connection(c.user_id)?
{
updated_page.remove_connection(&c.user_id)?;
}
}
let prid_result: DsnpGraphResult<Vec<_>> = updated_page
.connections()
.iter()
.map(|c| {
self.user_key_manager
.read()
.map_err(|_| DsnpGraphError::FailedtoReadLock(USER_KEY_MANAGER.to_string()))?
.calculate_prid(self.user_id, c.user_id, encryption_key.key_pair.clone().into())
})
.collect();
updated_page.set_prids(prid_result?)
}
#[log_result_err(Level::Info)]
pub fn try_add_connection_to_page(
&self,
page: &mut GraphPage,
connection_id: &DsnpUserId,
mode: PageFullnessMode,
dsnp_version_config: &DsnpVersionConfig,
encryption_key: &Option<ResolvedKeyPair>,
) -> DsnpGraphResult<()> {
let connection_type = self.get_connection_type();
let max_connections_per_page =
*PAGE_CAPACITY_MAP.get(&connection_type).unwrap_or_else(|| {
let mut capacities: Vec<&usize> = PAGE_CAPACITY_MAP.values().collect();
capacities.sort();
capacities.first().unwrap() });
if page.connections().len() < max_connections_per_page {
return page.add_connection(connection_id)
} else if mode == PageFullnessMode::Trivial {
return Err(DsnpGraphError::PageTriviallyFull)
}
let max_page_size = self.environment.get_config().max_graph_page_size_bytes as usize;
let mut temp_page = page.clone();
let _ = temp_page.add_connection(connection_id)?;
let page_blob = match connection_type {
ConnectionType::Follow(PrivacyType::Public) |
ConnectionType::Friendship(PrivacyType::Public) => temp_page.to_public_page_data(),
ConnectionType::Follow(PrivacyType::Private) => {
let encryption_key =
encryption_key.as_ref().ok_or(DsnpGraphError::NoResolvedActiveKeyFound)?;
temp_page.clear_prids();
temp_page.to_private_page_data(dsnp_version_config, &encryption_key)
},
ConnectionType::Friendship(PrivacyType::Private) => {
let encryption_key =
encryption_key.as_ref().ok_or(DsnpGraphError::NoResolvedActiveKeyFound)?;
self.apply_prids(&mut temp_page, &vec![*connection_id], &encryption_key)
.expect("Error applying prids to page");
temp_page.to_private_page_data(dsnp_version_config, &encryption_key)
},
};
match page_blob {
Ok(blob) =>
if blob.content.len() > max_page_size {
Err(DsnpGraphError::PageAggressivelyFull)
} else {
return page.add_connection(connection_id)
},
Err(e) => Err(e),
}
}
}
#[macro_export]
macro_rules! iter_graph_connections {
( $x:expr ) => {{
$x.pages()
.inner()
.values()
.flat_map(|p| p.connections().iter().cloned())
.collect::<Vec<DsnpGraphEdge>>()
.iter()
}};
}
#[cfg(test)]
mod test {
use super::*;
use crate::{
dsnp::dsnp_configs::KeyPairType,
graph::{
key_manager::{UserKeyManager, UserKeyProvider},
shared_state_manager::{PublicKeyProvider, SharedStateManager},
},
tests::{
helpers::{
add_public_key_for_dsnp_id, avro_public_payload, create_aggressively_full_page,
create_empty_test_graph, create_test_graph, create_test_ids_and_page,
create_trivially_full_page, get_env_and_config, INNER_TEST_DATA,
},
mocks::MockUserKeyManager,
},
util::builders::{GraphPageBuilder, KeyDataBuilder, PageDataBuilder},
};
use dryoc::keypair::StackKeyPair;
use dsnp_graph_config::{DsnpVersion, GraphKeyType, ALL_CONNECTION_TYPES};
use ntest::*;
#[allow(unused_imports)]
use pretty_assertions::{assert_eq, assert_ne, assert_str_eq};
#[test]
fn new_graph_is_empty() {
let env = Environment::Mainnet;
let user_id = 3;
let schema_id = env
.get_config()
.get_schema_id_from_connection_type(ConnectionType::Follow(PrivacyType::Private))
.expect("should exist");
let graph = Graph::new(
env,
user_id,
schema_id,
Arc::new(RwLock::new(UserKeyManager::new(
user_id,
Arc::new(RwLock::new(SharedStateManager::new())),
))),
);
assert_eq!(graph.pages().inner().is_empty(), true);
}
#[test]
fn graph_len_reports_number_of_connections() {
let graph = create_test_graph(None);
assert_eq!(graph.len(), 25);
}
#[test]
fn page_setter_sets_pages() {
let env = Environment::Mainnet;
let user_id = 3;
let schema_id = env
.get_config()
.get_schema_id_from_connection_type(ConnectionType::Follow(PrivacyType::Private))
.expect("should exist");
let mut pages = PageMap::new();
for i in 0..=1 {
let (_, p) = create_test_ids_and_page();
pages.insert(i, p);
}
let mut graph = Graph::new(
env,
user_id,
schema_id,
Arc::new(RwLock::new(UserKeyManager::new(
user_id,
Arc::new(RwLock::new(SharedStateManager::new())),
))),
);
graph.set_pages(pages.clone());
assert_eq!(pages.len(), graph.pages().len());
for i in 0..pages.len() as u16 {
assert_eq!(pages.get(&i), graph.pages().get(&i));
}
}
#[test]
fn get_next_available_page_id_returns_none_for_full_graph() {
let environment = Environment::Mainnet;
const CONN_TYPE: ConnectionType = ConnectionType::Follow(PrivacyType::Public);
const PRIV_TYPE: PrivacyType = CONN_TYPE.privacy_type();
let user_id = 3;
let schema_id = environment
.get_config()
.get_schema_id_from_connection_type(CONN_TYPE)
.expect("should exist");
let pages: PageMap = (0..=environment.get_config().max_page_id as PageId)
.map(|page_id: PageId| (page_id, GraphPage::new(PRIV_TYPE, page_id)))
.collect();
let graph = Graph {
environment,
schema_id, user_id,
pages,
user_key_manager: Arc::new(RwLock::new(UserKeyManager::new(
user_id,
Arc::new(RwLock::new(SharedStateManager::new())),
))),
};
assert_eq!(graph.get_next_available_page_id(&BTreeMap::default()), None);
}
#[test]
fn get_next_available_page_id_returns_correct_value() {
let environment = Environment::Mainnet;
let user_id = 3;
const CONN_TYPE: ConnectionType = ConnectionType::Follow(PrivacyType::Public);
const PRIV_TYPE: PrivacyType = CONN_TYPE.privacy_type();
let schema_id = environment
.get_config()
.get_schema_id_from_connection_type(CONN_TYPE)
.expect("should exist");
let mut pages: PageMap = (0..environment.get_config().max_page_id as PageId)
.map(|page_id: PageId| (page_id, GraphPage::new(PRIV_TYPE, page_id)))
.collect();
pages.remove(&8);
let graph = Graph {
environment,
schema_id, user_id,
pages,
user_key_manager: Arc::new(RwLock::new(UserKeyManager::new(
user_id,
Arc::new(RwLock::new(SharedStateManager::new())),
))),
};
assert_eq!(graph.get_next_available_page_id(&BTreeMap::default()), Some(8));
}
#[test]
fn get_next_available_page_should_include_updated_pages() {
let environment = Environment::Mainnet;
let user_id = 3;
const CONN_TYPE: ConnectionType = ConnectionType::Follow(PrivacyType::Public);
const PRIV_TYPE: PrivacyType = CONN_TYPE.privacy_type();
let schema_id = environment
.get_config()
.get_schema_id_from_connection_type(CONN_TYPE)
.expect("should exist");
let mut updated_pages: BTreeMap<_, _> = (0..environment.get_config().max_page_id as PageId)
.map(|page_id: PageId| (page_id, GraphPage::new(PRIV_TYPE, page_id)))
.collect();
updated_pages.remove(&8);
let graph = Graph {
environment,
schema_id, user_id,
pages: PageMap::new(),
user_key_manager: Arc::new(RwLock::new(UserKeyManager::new(
user_id,
Arc::new(RwLock::new(SharedStateManager::new())),
))),
};
assert_eq!(graph.get_next_available_page_id(&updated_pages), Some(8));
}
#[test]
fn clear_removes_all_pages() {
let mut graph = create_test_graph(None);
assert_eq!(graph.pages.len() > 0, true);
graph.clear();
assert_eq!(graph.pages.len(), 0);
}
#[test]
fn import_public_gets_correct_data() {
let environment = Environment::Mainnet;
let user_id = 3;
let connection_type = ConnectionType::Follow(PrivacyType::Public);
let schema_id = environment
.get_config()
.get_schema_id_from_connection_type(connection_type)
.expect("should exist");
let mut graph = Graph::new(
environment,
user_id,
schema_id,
Arc::new(RwLock::new(UserKeyManager::new(
user_id,
Arc::new(RwLock::new(SharedStateManager::new())),
))),
);
let blob = PageData { content_hash: 0, page_id: 0, content: avro_public_payload() };
let pages = vec![blob];
let _ = graph.import_public(connection_type, &pages);
assert_eq!(graph.pages.len(), 1);
let orig_connections: HashSet<DsnpUserId> =
INNER_TEST_DATA.iter().map(|edge| edge.user_id).collect();
let imported_connections: HashSet<DsnpUserId> =
iter_graph_connections!(graph).map(|edge| edge.user_id).collect();
assert_eq!(orig_connections, imported_connections);
}
#[test]
fn import_private_follow_gets_correct_data() {
let connection_type = ConnectionType::Follow(PrivacyType::Private);
let user_id = 3;
let environment = Environment::Mainnet;
let schema_id = environment
.get_config()
.get_schema_id_from_connection_type(connection_type)
.expect("should exist");
let shared_state_manager = Arc::new(RwLock::new(SharedStateManager::new()));
let user_key_manager =
Arc::new(RwLock::new(UserKeyManager::new(user_id, shared_state_manager.clone())));
let mut graph = Graph::new(environment, user_id, schema_id, user_key_manager.clone());
let raw_key_pair = StackKeyPair::gen();
let resolved_key =
ResolvedKeyPair { key_pair: KeyPairType::Version1_0(raw_key_pair.clone()), key_id: 1 };
let dsnp_config = DsnpVersionConfig::new(DsnpVersion::Version1_0);
let orig_connections: HashSet<DsnpUserId> =
INNER_TEST_DATA.iter().map(|edge| edge.user_id).collect();
let pages = PageDataBuilder::new(connection_type)
.with_encryption_key(resolved_key.clone())
.with_page(0, &orig_connections.iter().map(|u| (*u, 0)).collect::<Vec<_>>(), &vec![], 0)
.build();
let graph_key_pair = GraphKeyPair {
key_type: GraphKeyType::X25519,
secret_key: raw_key_pair.secret_key.to_vec(),
public_key: raw_key_pair.public_key.to_vec(),
};
let dsnp_keys = DsnpKeys {
keys: KeyDataBuilder::new().with_key_pairs(&vec![graph_key_pair.clone()]).build(),
dsnp_user_id: user_id,
keys_hash: 0,
};
shared_state_manager
.write()
.unwrap()
.import_dsnp_keys(&dsnp_keys)
.expect("should succeed");
user_key_manager
.write()
.unwrap()
.import_key_pairs(vec![graph_key_pair])
.expect("should succeed");
let res = graph.import_private(&dsnp_config, connection_type, &pages);
assert!(res.is_ok());
assert_eq!(graph.pages.len(), 1);
let imported_connections: HashSet<DsnpUserId> =
iter_graph_connections!(graph).map(|edge| edge.user_id).collect();
assert_eq!(orig_connections, imported_connections);
}
#[test]
fn create_page_with_existing_pageid_fails() {
let mut graph = create_test_graph(None);
assert_eq!(graph.create_page(&0, None).is_err(), true);
}
#[test]
fn create_page_succeeds() {
let environment = Environment::Mainnet;
let user_id = 3;
let schema_id = environment
.get_config()
.get_schema_id_from_connection_type(ConnectionType::Follow(PrivacyType::Private))
.expect("should exist");
let (_, page) = create_test_ids_and_page();
let mut graph = Graph::new(
environment,
user_id,
schema_id,
Arc::new(RwLock::new(UserKeyManager::new(
user_id,
Arc::new(RwLock::new(SharedStateManager::new())),
))),
);
assert_eq!(graph.create_page(&0, Some(page.clone())).is_ok(), true);
assert_eq!(page, *graph.get_page(&0).unwrap());
}
#[test]
fn has_connection_returns_false_for_missing_connection() {
let graph = create_test_graph(None);
assert_eq!(graph.has_connection(&99), false);
}
#[test]
fn has_connection_returns_true_for_present_connection() {
let graph = create_test_graph(None);
assert_eq!(graph.has_connection(&1), true);
}
#[test]
fn find_connection_returns_none_for_nonexistent_connection() {
let graph = create_test_graph(None);
assert_eq!(graph.find_connection(&99), None);
}
#[test]
fn find_connections_returns_pageid_of_existing_connection() {
let graph = create_test_graph(None);
assert_eq!(graph.find_connection(&1), Some(0));
}
#[test]
fn find_connections_returns_vec_of_pageids() {
let graph = create_test_graph(None);
let mut v = graph.find_connections(&vec![1, 5, 24]);
v.sort();
assert_eq!(v, vec![0, 1, 4]);
}
#[test]
fn add_connection_duplicate_connection_errors() {
let mut graph = create_test_graph(None);
assert_eq!(graph.add_connection_to_page(&4, &0).is_err(), true);
}
#[test]
fn add_connection_to_nonexistent_page_adds_new_page() {
let mut graph = create_test_graph(None);
let page_to_add: PageId = 99;
assert_eq!(graph.pages().inner().contains_key(&page_to_add), false);
let _ = graph.add_connection_to_page(&page_to_add, &12345);
assert_eq!(graph.pages().inner().contains_key(&page_to_add), true);
}
#[test]
fn add_connection_succeeds() {
let mut graph = create_test_graph(None);
let _ = graph.add_connection_to_page(&4, &99);
assert_eq!(graph.find_connection(&99), Some(4));
}
#[test]
fn remove_connection_returns_none_for_not_found() {
let mut graph = create_test_graph(None);
let result = graph.remove_connection(&99);
assert_eq!(result.unwrap().is_none(), true);
}
#[test]
fn remove_connection_returns_pageid_of_removed_connection() {
let mut graph = create_test_graph(None);
let result = graph.remove_connection(&5);
assert_eq!(result.unwrap(), Some(1));
}
#[test]
fn graph_iterator_should_iterate_over_all_connections() {
let graph = create_test_graph(None);
let mut test_connections: Vec<DsnpUserId> = (0..25).map(|i| i as DsnpUserId).collect();
test_connections.sort();
let mut graph_connections: Vec<DsnpUserId> =
iter_graph_connections!(graph).map(|edge| edge.user_id).collect();
graph_connections.sort();
assert_eq!(test_connections, graph_connections);
}
fn updates_to_page(updates: &[Update]) -> Vec<PageData> {
updates
.iter()
.filter_map(|u| match u {
Update::PersistPage { page_id, payload, .. } =>
Some(PageData { page_id: *page_id, content_hash: 0, content: payload.clone() }),
_ => None,
})
.collect()
}
#[test]
#[timeout(5000)] fn calculate_updates_public_existing_pages_succeeds() {
let connection_type = ConnectionType::Follow(PrivacyType::Public);
let ids_per_page = 5;
let user_id = 3;
let mut curr_id = 1u64;
let mut page_builder = GraphPageBuilder::new(connection_type);
for i in 0..5 {
let ids: Vec<(DsnpUserId, u64)> =
(curr_id..(curr_id + ids_per_page)).map(|id| (id, 0)).collect();
page_builder = page_builder.with_page(i, &ids, &vec![], 0);
curr_id += ids_per_page;
}
let env = Environment::Mainnet;
let schema_id = env
.get_config()
.get_schema_id_from_connection_type(connection_type)
.expect("should exist");
let mut graph = Graph::new(
env,
user_id,
schema_id,
Arc::new(RwLock::new(UserKeyManager::new(
user_id,
Arc::new(RwLock::new(SharedStateManager::new())),
))),
);
for p in page_builder.build() {
let _ = graph.create_page(&p.page_id(), Some(p)).expect("should create page!");
}
let updates = vec![
UpdateEvent::create_remove(1, graph.schema_id),
UpdateEvent::create_remove(1 + ids_per_page * 2, graph.schema_id),
UpdateEvent::create_add(curr_id + 1, graph.schema_id),
UpdateEvent::create_add(curr_id + 2, graph.schema_id),
];
let updates =
graph.calculate_updates(&DsnpVersionConfig::new(DsnpVersion::Version1_0), &updates);
assert!(updates.is_ok());
let updates = updates.unwrap();
assert_eq!(updates.len(), 2);
graph
.import_public(connection_type, &updates_to_page(&updates))
.expect("should import");
let removed_connection_1 = graph.find_connection(&1);
let removed_connection_2 = graph.find_connection(&(1 + ids_per_page * 2));
assert!(removed_connection_1.is_none());
assert!(removed_connection_2.is_none());
let added_connection_1 = graph.find_connection(&(curr_id + 1));
let added_connection_2 = graph.find_connection(&(curr_id + 2));
assert_eq!(added_connection_1, Some(0));
assert_eq!(added_connection_2, Some(0));
}
#[log_result_err(Level::Info)]
fn calculate_updates_adding_new_page(connection_type: ConnectionType) -> DsnpGraphResult<()> {
let (_, dsnp_version_config) = get_env_and_config();
let user_id = 3u64;
let mut curr_id = 100u64;
let (mut graph, _, shared_state) =
create_empty_test_graph(Some(user_id), Some(connection_type));
for _ in 0..2 {
let page_id = create_aggressively_full_page(
&mut graph,
curr_id,
&dsnp_version_config,
&shared_state,
);
let page = graph
.get_page(&page_id)
.expect(format!("error returning page {} from graph", page_id).as_str());
let last_id = page
.connections()
.last()
.expect(
format!("page should have at least one connection ({:?})", connection_type)
.as_str(),
)
.user_id;
curr_id = last_id + 1;
}
let mut updates: Vec<UpdateEvent> = Vec::new();
for i in 1..=2 {
if connection_type == ConnectionType::Friendship(PrivacyType::Private) {
let dsnp_keys = DsnpKeys {
dsnp_user_id: curr_id + i,
keys_hash: 0,
keys: KeyDataBuilder::new().with_generated_key().build(),
};
shared_state
.write()
.unwrap()
.import_dsnp_keys(&dsnp_keys)
.expect("failed to import public keys");
}
updates.push(UpdateEvent::create_add(curr_id + i, graph.schema_id));
}
let updates = graph.calculate_updates(&dsnp_version_config, &updates);
assert!(updates.is_ok(), "[{:?}] calculate_updates failed: {:?}", updates, connection_type,);
let updates = updates.unwrap();
assert_eq!(updates.len(), 1, "Updates should contain 1 page ({:?})", connection_type);
if let Update::PersistPage { page_id, .. } = updates.first().unwrap() {
assert!(*page_id == 2, "Update should be page 2");
} else {
panic!("Update is not a PersistPage");
}
match connection_type.privacy_type() {
PrivacyType::Public =>
graph.import_public(connection_type, &updates_to_page(&updates)).expect(
format!("failed to re-import exported graph ({:?})", connection_type).as_str(),
),
PrivacyType::Private => graph
.import_private(&dsnp_version_config, connection_type, &updates_to_page(&updates))
.expect(
format!("failed to re-import exported graph ({:?})", connection_type).as_str(),
),
}
let added_connection_1 = graph.find_connection(&(curr_id + 1));
let added_connection_2 = graph.find_connection(&(curr_id + 2));
assert_eq!(
added_connection_1,
Some(2),
"Updated page id should be 2 ({:?})",
connection_type
);
assert_eq!(
added_connection_2,
Some(2),
"Updated page id should be 2 ({:?})",
connection_type
);
Ok(())
}
#[log_result_err(Level::Info)]
fn calculate_updates_existing_page(connection_type: ConnectionType) -> DsnpGraphResult<()> {
let (_, dsnp_version_config) = get_env_and_config();
let user_id = 3u64;
let starting_id = 100u64;
let mut curr_id = starting_id;
let (mut graph, _, shared_state) =
create_empty_test_graph(Some(user_id), Some(connection_type));
for _ in 0..2 {
let page_id = create_aggressively_full_page(
&mut graph,
curr_id,
&dsnp_version_config,
&shared_state,
);
let page = graph
.get_page(&page_id)
.expect(format!("error returning page {} from graph", page_id).as_str());
let last_id = page
.connections()
.last()
.expect(
format!("page should have at least one connection ({:?})", connection_type)
.as_str(),
)
.user_id;
curr_id = last_id + 1;
}
let page = graph
.get_page_mut(&0)
.expect(format!("error returning page {} from graph", 0).as_str());
page.set_connections(
page.connections()
.iter()
.enumerate()
.filter_map(|(ref index, ref c)| if *index >= 10 { Some(**c) } else { None })
.collect(),
);
let mut updates: Vec<UpdateEvent> = Vec::new();
for _ in 1..=2 {
if connection_type == ConnectionType::Friendship(PrivacyType::Private) {
add_public_key_for_dsnp_id(curr_id, &shared_state);
}
updates.push(UpdateEvent::create_add(curr_id, graph.schema_id));
curr_id += 1;
}
let update_blobs = graph.calculate_updates(&dsnp_version_config, &updates);
assert!(
update_blobs.is_ok(),
"[{:?}] calculate_updates failed: {:?}",
update_blobs,
connection_type,
);
let update_blobs = update_blobs.unwrap();
assert_eq!(update_blobs.len(), 1, "Updates should contain 1 page ({:?})", connection_type);
update_blobs.iter().for_each(|u| {
if let Update::PersistPage { page_id, .. } = u {
assert!(*page_id == 0, "Update should be page 0, was page {}", page_id);
} else {
assert!(false, "Update is not a PersistPage");
}
});
match connection_type.privacy_type() {
PrivacyType::Public =>
graph.import_public(connection_type, &updates_to_page(&update_blobs)).expect(
format!("failed to re-import exported graph ({:?})", connection_type).as_str(),
),
PrivacyType::Private => graph
.import_private(
&dsnp_version_config,
connection_type,
&updates_to_page(&update_blobs),
)
.expect(
format!("failed to re-import exported graph ({:?})", connection_type).as_str(),
),
}
updates.iter().for_each(|u| {
if let UpdateEvent::Add { dsnp_user_id, .. } = u {
let added_connection = graph.find_connection(dsnp_user_id).expect(
format!(
"Graph did not contain added connection {} ({:?})",
dsnp_user_id, connection_type
)
.as_str(),
);
assert_eq!(
added_connection, 0,
"Updated page id should be 0 ({:?})",
connection_type
);
}
});
Ok(())
}
#[test]
#[timeout(15000)] fn calculate_updates_adding_new_page_public_follow_should_succeed() {
calculate_updates_adding_new_page(ConnectionType::Follow(PrivacyType::Public))
.expect("should succeed");
}
#[test]
#[timeout(15000)] fn calculate_updates_adding_new_page_private_follow_should_succeed() {
calculate_updates_adding_new_page(ConnectionType::Follow(PrivacyType::Private))
.expect("should succeed");
}
#[test]
#[timeout(15000)] fn calculate_updates_adding_new_page_private_friendship_should_succeed() {
calculate_updates_adding_new_page(ConnectionType::Friendship(PrivacyType::Private))
.expect("should succeed");
}
#[test]
#[timeout(15000)]
fn calculate_updates_existing_page_public_follow_should_succeed() {
calculate_updates_existing_page(ConnectionType::Follow(PrivacyType::Public))
.expect("should succeed");
}
#[test]
#[timeout(15000)]
fn calculate_updates_existing_page_private_follow_should_succeed() {
calculate_updates_existing_page(ConnectionType::Follow(PrivacyType::Private))
.expect("should succeed");
}
#[test]
#[timeout(15000)]
fn calculate_updates_existing_page_private_friendship_should_succeed() {
calculate_updates_existing_page(ConnectionType::Friendship(PrivacyType::Private))
.expect("should succeed");
}
#[test]
fn get_one_sided_friendships_should_return_expected_connections() {
let connection_type = ConnectionType::Friendship(PrivacyType::Private);
let env = Environment::Mainnet;
let schema_id = env
.get_config()
.get_schema_id_from_connection_type(connection_type)
.expect("should exist");
let mut key_manager = MockUserKeyManager::new();
let max_connections = PAGE_CAPACITY_MAP.get(&connection_type).unwrap();
let ids: Vec<(DsnpUserId, u64)> =
(1..*max_connections as u64 as DsnpUserId).map(|u| (u, 0)).collect();
let verifications: Vec<_> = ids.iter().map(|(id, _)| (*id, Some(true))).collect();
key_manager.register_verifications(&verifications);
key_manager.register_verifications(&vec![(1, Some(false)), (2, Some(false))]);
let mut graph = Graph::new(env, 1000, schema_id, Arc::new(RwLock::new(key_manager)));
for p in GraphPageBuilder::new(connection_type)
.with_page(1, &ids, &vec![DsnpPrid::new(&[0, 1, 2, 3, 4, 5, 6, 7]); ids.len()], 0)
.build()
{
let _ = graph.create_page(&p.page_id(), Some(p)).expect("should create page!");
}
let one_sided = graph.get_one_sided_friendships();
assert!(one_sided.is_ok());
let one_sided = one_sided.unwrap();
assert_eq!(
one_sided,
vec![DsnpGraphEdge { user_id: 1, since: 0 }, DsnpGraphEdge { user_id: 2, since: 0 }]
);
}
#[test]
fn private_friendship_functions_should_fail_for_non_private_friendship_graphs() {
let env = Environment::Mainnet;
let failures = vec![
ConnectionType::Follow(PrivacyType::Private),
ConnectionType::Follow(PrivacyType::Public),
];
for connection_type in failures {
let schema_id = env
.get_config()
.get_schema_id_from_connection_type(connection_type)
.expect("should exist");
let graph = Graph::new(
env.clone(),
1000,
schema_id,
Arc::new(RwLock::new(MockUserKeyManager::new())),
);
let one_sided = graph.get_one_sided_friendships();
let prids = graph.apply_prids(
&mut GraphPage::new(connection_type.privacy_type(), 1),
&vec![],
&ResolvedKeyPair {
key_id: 1,
key_pair: KeyPairType::Version1_0(StackKeyPair::gen()),
},
);
assert!(one_sided.is_err());
assert!(prids.is_err());
}
}
#[test]
fn get_one_sided_friendships_with_key_related_errors_should_fail() {
let connection_type = ConnectionType::Friendship(PrivacyType::Private);
let env = Environment::Mainnet;
let schema_id = env
.get_config()
.get_schema_id_from_connection_type(connection_type)
.expect("should exist");
let mut key_manager = MockUserKeyManager::new();
let max_connections = PAGE_CAPACITY_MAP.get(&connection_type).unwrap();
let ids: Vec<(DsnpUserId, u64)> =
(1..*max_connections as u64 as DsnpUserId).map(|id| (id, 0)).collect();
let verifications: Vec<_> = ids.iter().map(|(id, _)| (*id, Some(true))).collect();
key_manager.register_verifications(&verifications);
key_manager.register_verifications(&vec![(2, None)]);
let mut graph = Graph::new(env, 1000, schema_id, Arc::new(RwLock::new(key_manager)));
for p in GraphPageBuilder::new(connection_type)
.with_page(1, &ids, &vec![DsnpPrid::new(&[0, 1, 2, 3, 4, 5, 6, 7]); ids.len()], 0)
.build()
{
let _ = graph.create_page(&p.page_id(), Some(p)).expect("should create page!");
}
let one_sided = graph.get_one_sided_friendships();
assert!(one_sided.is_err());
}
#[test]
fn trivial_add_to_trivially_non_full_page_succeeds() {
let (_, dsnp_version_config) = get_env_and_config();
ALL_CONNECTION_TYPES.iter().for_each(|c| {
let (graph, ..) = create_empty_test_graph(None, Some(*c));
let max_connections_per_page = PAGE_CAPACITY_MAP
.get(c)
.expect("Connection type missing max connections soft limit");
let builder = GraphPageBuilder::new(*c).with_page(1, &[], &[], 0);
let mut pages = builder.build();
let page = pages.first_mut().expect("Should have created page");
for i in 1u64..*max_connections_per_page as u64 {
assert!(
graph
.try_add_connection_to_page(
page,
&i,
PageFullnessMode::Trivial,
&dsnp_version_config,
&None
)
.is_ok(),
"Testing soft connection limit for {:?}",
c,
);
}
});
}
#[test]
fn trivial_add_to_trivially_full_page_fails() {
let (_, ref dsnp_version_config) = get_env_and_config();
ALL_CONNECTION_TYPES.iter().for_each(|c| {
let (graph, ..) = create_empty_test_graph(None, Some(*c));
let mut page = create_trivially_full_page(*c, 0, 100);
let conn_id = page.connections().iter().map(|edge| edge.user_id).max().unwrap() + 1;
assert!(
graph
.try_add_connection_to_page(
&mut page,
&conn_id,
PageFullnessMode::Trivial,
dsnp_version_config,
&None
)
.is_err(),
"Testing soft connection limit for {:?}",
c
);
});
}
#[test]
fn aggressive_add_to_trivially_non_full_page_succeeds() {
let (_, ref dsnp_version_config) = get_env_and_config();
ALL_CONNECTION_TYPES.iter().for_each(|c| {
let (graph, ..) = create_empty_test_graph(None, Some(*c));
let mut page = create_trivially_full_page(*c, 0, 100);
let mut conn_id = page.connections().iter().map(|edge| edge.user_id).max().unwrap();
page.remove_connection(&conn_id).expect("failed to remove connection");
conn_id += 1;
assert!(
graph
.try_add_connection_to_page(
&mut page,
&conn_id,
PageFullnessMode::Aggressive,
dsnp_version_config,
&None
)
.is_ok(),
"Testing aggressive add to trivially non-full page for {:?}",
c
);
});
}
#[test]
fn aggressive_add_to_trivially_full_page_succeeds() {
ALL_CONNECTION_TYPES.iter().for_each(|c| {
let (graph, _, shared_state) = create_empty_test_graph(None, Some(*c));
let (_, dsnp_version_config) = get_env_and_config();
let encryption_key =
graph.get_user_key_mgr().read().unwrap().get_resolved_active_key(graph.user_id);
let mut page = create_trivially_full_page(*c, 0, 100);
let conn_id_to_add =
page.connections().iter().map(|edge| edge.user_id).max().unwrap() + 1;
if *c == ConnectionType::Friendship(PrivacyType::Private) {
page.connections()
.iter()
.for_each(|c| add_public_key_for_dsnp_id(c.user_id, &shared_state));
add_public_key_for_dsnp_id(conn_id_to_add, &shared_state);
}
assert!(
graph
.try_add_connection_to_page(
&mut page,
&conn_id_to_add,
PageFullnessMode::Aggressive,
&dsnp_version_config,
&encryption_key
)
.is_ok(),
"Testing aggressive add to trivially full page for {:?}",
c
);
});
}
#[test]
fn aggressive_add_to_aggressively_full_page_fails() {
ALL_CONNECTION_TYPES.iter().for_each(|c| {
let (mut graph, _, shared_state) = create_empty_test_graph(None, Some(*c));
let (_, dsnp_version_config) = get_env_and_config();
let encryption_key =
graph.get_user_key_mgr().read().unwrap().get_resolved_active_key(graph.user_id);
let page_id =
create_aggressively_full_page(&mut graph, 100, &dsnp_version_config, &shared_state);
let mut page = graph.get_page_mut(&page_id).expect("unable to retrieve page").clone();
let conn_id_to_add =
page.connections().iter().map(|edge| edge.user_id).max().unwrap() + 1;
if *c == ConnectionType::Friendship(PrivacyType::Private) {
page.connections()
.iter()
.for_each(|c| add_public_key_for_dsnp_id(c.user_id, &shared_state));
add_public_key_for_dsnp_id(conn_id_to_add, &shared_state);
}
assert!(
graph
.try_add_connection_to_page(
&mut page,
&conn_id_to_add,
PageFullnessMode::Aggressive,
&dsnp_version_config,
&encryption_key
)
.is_err(),
"Testing aggressive add to aggressively full page for {:?}",
c
);
});
}
#[test]
fn graph_page_rollback_should_revert_changes_on_graph_and_all_underlying_page() {
let connection_type = ConnectionType::Friendship(PrivacyType::Private);
let env = Environment::Mainnet;
let mut graph =
Graph::new(env, 1000, 2000, Arc::new(RwLock::new(MockUserKeyManager::new())));
let mut page_1 = GraphPage::new(connection_type.privacy_type(), 1);
let connection_dsnp = 900;
page_1.add_connection(&connection_dsnp).unwrap();
graph.pages.insert(1, page_1.clone());
graph.commit();
let page_1 = graph.pages.get_mut(&1).unwrap();
page_1.remove_connection(&connection_dsnp).unwrap();
page_1.add_connection(&500).unwrap();
let mut page_2 = GraphPage::new(connection_type.privacy_type(), 2);
page_2.add_connection(&400).unwrap();
graph.create_page(&2, Some(page_2)).unwrap();
graph.rollback();
assert_eq!(graph.pages.len(), 1);
assert_eq!(graph.pages.get(&1).unwrap().connections().len(), 1);
assert!(graph.pages.get(&1).unwrap().contains(&connection_dsnp));
}
#[test]
fn force_recalculate_public_should_work_as_expected() {
let connection_type = ConnectionType::Follow(PrivacyType::Public);
let env = Environment::Mainnet;
let schema_id = env
.get_config()
.get_schema_id_from_connection_type(connection_type)
.expect("should exist");
let user_id = 1000;
let ids: Vec<_> = (1..50).map(|u| (u, 0)).collect();
let pages = GraphPageBuilder::new(connection_type).with_page(1, &ids, &vec![], 0).build();
let mut graph =
Graph::new(env, user_id, schema_id, Arc::new(RwLock::new(MockUserKeyManager::new())));
for (i, p) in pages.into_iter().enumerate() {
let _ = graph.create_page(&(i as PageId), Some(p));
}
let updates = graph.force_recalculate(&DsnpVersionConfig::new(DsnpVersion::Version1_0));
assert!(updates.is_ok());
let updates = updates.unwrap();
assert_eq!(updates.len(), 1);
assert!(matches!(updates.get(0).unwrap(), Update::PersistPage { .. }));
}
#[test]
fn force_recalculate_private_follow_should_work_as_expected() {
let connection_type = ConnectionType::Follow(PrivacyType::Private);
let env = Environment::Mainnet;
let schema_id = env
.get_config()
.get_schema_id_from_connection_type(connection_type)
.expect("should exist");
let user_id = 1000;
let ids: Vec<_> = (1..50).map(|u| (u, 0)).collect();
let pages = GraphPageBuilder::new(connection_type).with_page(1, &ids, &vec![], 0).build();
let key =
ResolvedKeyPair { key_id: 1, key_pair: KeyPairType::Version1_0(StackKeyPair::gen()) };
let mut key_manager = MockUserKeyManager::new();
key_manager.register_key(user_id, &key);
let mut graph = Graph::new(env, user_id, schema_id, Arc::new(RwLock::new(key_manager)));
for (i, p) in pages.into_iter().enumerate() {
let _ = graph.create_page(&(i as PageId), Some(p));
}
let updates = graph.force_recalculate(&DsnpVersionConfig::new(DsnpVersion::Version1_0));
assert!(updates.is_ok());
let updates = updates.unwrap();
assert_eq!(updates.len(), 1);
assert!(matches!(updates.get(0).unwrap(), Update::PersistPage { .. }));
}
#[test]
fn force_recalculate_private_friendship_should_work_as_expected() {
let connection_type = ConnectionType::Friendship(PrivacyType::Private);
let env = Environment::Mainnet;
let schema_id = env
.get_config()
.get_schema_id_from_connection_type(connection_type)
.expect("should exist");
let user_id = 1000;
let ids: Vec<_> = (1..50).map(|u| (u, 0)).collect();
let pages = GraphPageBuilder::new(connection_type)
.with_page(1, &ids, &vec![DsnpPrid::new(&[0, 1, 2, 3, 4, 5, 6, 7]); ids.len()], 0)
.build();
let key =
ResolvedKeyPair { key_id: 1, key_pair: KeyPairType::Version1_0(StackKeyPair::gen()) };
let mut key_manager = MockUserKeyManager::new();
key_manager.register_key(user_id, &key);
let verifications: Vec<_> = ids.iter().map(|(id, _)| (*id, Some(true))).collect();
key_manager.register_verifications(&verifications);
let mut graph = Graph::new(env, user_id, schema_id, Arc::new(RwLock::new(key_manager)));
for (i, p) in pages.into_iter().enumerate() {
let _ = graph.create_page(&(i as PageId), Some(p));
}
let updates = graph.force_recalculate(&DsnpVersionConfig::new(DsnpVersion::Version1_0));
assert!(updates.is_ok());
let updates = updates.unwrap();
assert_eq!(updates.len(), 1);
assert!(matches!(updates.get(0).unwrap(), Update::PersistPage { .. }));
}
}