mod coins;
mod sequence;
mod unpublished_mutable_data;
use crate::test_utils::{create_app, create_random_auth_req, gen_app_exchange_info};
use crate::test_utils::{create_app_by_req, create_auth_req_with_access};
use crate::{App, AppError};
use log::trace;
use safe_authenticator::test_utils as authenticator_test_utils;
use safe_authenticator::Authenticator;
use safe_core::btree_set;
use safe_core::ipc::req::{AppExchangeInfo, AuthReq};
use safe_core::ipc::Permission;
use safe_core::utils;
use safe_core::utils::test_utils::random_client;
#[cfg(feature = "mock-network")]
use safe_core::ConnectionManager;
use safe_core::{client::COST_OF_PUT, Client, CoreError};
use safe_nd::{AppPermissions, Coins, Error as SndError, IData, PubImmutableData};
#[cfg(feature = "mock-network")]
use safe_nd::{RequestType, Response};
use std::collections::{BTreeMap, HashMap};
use unwrap::unwrap;
use xor_name::XorName;
#[tokio::test]
async fn refresh_access_info() -> Result<(), AppError> {
let mut container_permissions = HashMap::new();
let _ = container_permissions.insert(
"_videos".to_string(),
btree_set![Permission::Read, Permission::Insert],
);
let app =
create_app_by_req(&create_auth_req_with_access(container_permissions.clone())).await?;
let client = app.client;
let reg = app.context.as_registered()?;
assert!(reg.access_info.lock().await.is_empty());
app.context.refresh_access_info(&client).await?;
let access_info = reg.access_info.lock().await;
assert_eq!(
unwrap!(access_info.get("_videos")).1,
*unwrap!(container_permissions.get("_videos"))
);
Ok(())
}
#[tokio::test]
async fn get_access_info() -> Result<(), AppError> {
let mut container_permissions = HashMap::new();
let _ = container_permissions.insert("_videos".to_string(), btree_set![Permission::Read]);
let _ = container_permissions.insert("_downloads".to_string(), btree_set![Permission::Insert]);
let auth = authenticator_test_utils::create_account_and_login().await;
let app_info = gen_app_exchange_info();
let app_id = app_info.id.clone();
let auth_granted = authenticator_test_utils::register_app(
&auth,
&AuthReq {
app: app_info,
app_container: true,
app_permissions: AppPermissions {
transfer_coins: true,
perform_mutations: true,
get_balance: true,
},
containers: container_permissions,
},
)
.await
.map_err(|_| AppError::Unexpected("failed to obtain a registered app".to_string()))?;
let app = App::registered(app_id, auth_granted, || ()).await?;
let client = app.client;
let info = app.context.get_access_info(&client).await?;
assert!(info.contains_key(&"_videos".to_string()));
assert!(info.contains_key(&"_downloads".to_string()));
assert_eq!(info.len(), 3);
let (ref _md_info, ref perms) = info["_videos"];
assert_eq!(perms, &btree_set![Permission::Read]);
let (ref _md_info, ref perms) = info["_downloads"];
assert_eq!(perms, &btree_set![Permission::Insert]);
Ok(())
}
#[cfg(feature = "mock-network")]
#[tokio::test]
pub async fn login_registered_with_low_balance() -> Result<(), AppError> {
let cm_hook = move |mut cm: ConnectionManager| -> ConnectionManager {
cm.set_request_hook(move |req| {
if req.get_type() == RequestType::Mutation {
Some(Response::Mutation(Err(SndError::InsufficientBalance)))
} else {
None
}
});
cm
};
let auth = authenticator_test_utils::create_account_and_login().await;
let app_info = gen_app_exchange_info();
let app_id = app_info.id.clone();
let auth_granted = authenticator_test_utils::register_app(
&auth,
&AuthReq {
app: app_info,
app_container: false,
app_permissions: Default::default(),
containers: HashMap::new(),
},
)
.await
.map_err(|_| AppError::Unexpected("failed to obtain a registered app".to_string()))?;
let _app = App::registered_with_hook(app_id, auth_granted, || (), cm_hook)?;
Ok(())
}
async fn authorise_app(
auth: &Authenticator,
app_info: &AppExchangeInfo,
app_id: &str,
app_container: bool,
) -> Result<App, AppError> {
let auth_granted = authenticator_test_utils::register_app(
auth,
&AuthReq {
app: app_info.clone(),
app_container,
app_permissions: AppPermissions {
transfer_coins: true,
perform_mutations: true,
get_balance: true,
},
containers: HashMap::new(),
},
)
.await
.map_err(|_| AppError::Unexpected("failed to obtain a registered app".to_string()))?;
App::registered(String::from(app_id), auth_granted, || ()).await
}
async fn num_containers(app: &App) -> Result<usize, AppError> {
trace!("Getting the number of containers.");
let info = app.context.get_access_info(&app.client).await?;
Ok(info.len())
}
#[tokio::test]
async fn app_container_creation() -> Result<(), AppError> {
trace!("Authorising an app for the first time with `app_container` set to `true`.");
{
let auth = authenticator_test_utils::create_account_and_login().await;
let app_info = gen_app_exchange_info();
let app_id = app_info.id.clone();
let app = authorise_app(&auth, &app_info, &app_id, true).await?;
assert_eq!(num_containers(&app).await?, 1); }
trace!("Authorising a new app with `app_container` set to `false`.");
let auth = authenticator_test_utils::create_account_and_login().await;
let app_info = gen_app_exchange_info();
let app_id = app_info.id.clone();
{
let app = authorise_app(&auth, &app_info, &app_id, false).await?;
assert_eq!(num_containers(&app).await?, 0); }
trace!("Re-authorising the app with `app_container` set to `true`.");
{
let app = authorise_app(&auth, &app_info, &app_id, true).await?;
assert_eq!(num_containers(&app).await?, 1); }
trace!("Making sure no mutations are done when re-authorising the app now.");
let orig_balance: Coins = auth.client.get_balance(None).await?;
let _ = authorise_app(&auth, &app_info, &app_id, true);
let new_balance: Coins = auth.client.get_balance(None).await?;
assert_eq!(orig_balance, new_balance);
trace!("Authorising a new app with `app_container` set to `false`.");
let auth = authenticator_test_utils::create_account_and_login().await;
let app_info = gen_app_exchange_info();
let app_id = app_info.id.clone();
{
let app = authorise_app(&auth, &app_info, &app_id, false).await?;
assert_eq!(num_containers(&app).await?, 0); }
trace!("Revoking the app.");
authenticator_test_utils::revoke(&auth, &app_id).await;
trace!("Re-authorising the app with `app_container` set to `true`.");
{
let app = authorise_app(&auth, &app_info, &app_id, true).await?;
assert_eq!(num_containers(&app).await?, 1); }
Ok(())
}
#[tokio::test]
async fn unregistered_client() -> Result<(), AppError> {
let pub_idata_content = utils::generate_random_vector(30)?;
let (pub_idata_addr, pub_sdata_addr, priv_sdata_addr) = {
let client = random_client()?;
let pub_idata = PubImmutableData::new(pub_idata_content.clone());
let pub_idata_addr = *pub_idata.address();
client.put_idata(pub_idata).await?;
let name: XorName = rand::random();
let tag = 15002;
let owner = client.owner_key().await;
let pub_sdata_addr = client
.store_pub_sdata(name, tag, owner, BTreeMap::default())
.await?;
let priv_sdata_addr = client
.store_priv_sdata(name, tag, owner, BTreeMap::default())
.await?;
(pub_idata_addr, pub_sdata_addr, priv_sdata_addr)
};
let app = App::unregistered(|| (), None).await?;
let data = app.client.get_idata(pub_idata_addr).await?;
assert_eq!(data, IData::Pub(PubImmutableData::new(pub_idata_content)));
let data = app.client.get_sdata(pub_sdata_addr).await?;
assert_eq!(*data.address(), pub_sdata_addr);
match app.client.get_sdata(priv_sdata_addr).await {
Err(CoreError::DataError(SndError::AccessDenied)) => (),
res => panic!("Unexpected result {:?}", res),
}
Ok(())
}
#[tokio::test]
async fn unregistered_client_put() -> Result<(), AppError> {
let pub_idata = PubImmutableData::new(utils::generate_random_vector(30)?);
let app = App::unregistered(|| (), None).await?;
let client = app.client;
match client.put_idata(pub_idata).await {
Err(CoreError::DataError(SndError::AccessDenied)) => {}
Ok(()) => panic!("Unexpected Success"),
Err(e) => panic!("Unexpected Error: {}", e),
}
Ok(())
}
#[tokio::test]
async fn public_data_access() -> Result<(), AppError> {
let pub_idata_content = utils::generate_random_vector(30)?;
let (pub_idata_addr, pub_sdata_addr) = {
let client = random_client()?;
let pub_idata = PubImmutableData::new(pub_idata_content.clone());
let pub_idata_addr = *pub_idata.address();
client.put_idata(pub_idata).await?;
let name: XorName = rand::random();
let tag = 15002;
let owner = client.owner_key().await;
let perms = BTreeMap::default();
let pub_sdata_addr = client.store_pub_sdata(name, tag, owner, perms).await?;
(pub_idata_addr, pub_sdata_addr)
};
{
let app = App::unregistered(|| (), None).await?;
let client = app.client;
let data = client.get_idata(pub_idata_addr).await?;
assert_eq!(
data,
IData::Pub(PubImmutableData::new(pub_idata_content.clone()))
);
let data = client.get_sdata(pub_sdata_addr).await?;
assert_eq!(*data.address(), pub_sdata_addr);
}
let app = create_app().await;
let data = app.client.get_idata(pub_idata_addr).await?;
assert_eq!(data, IData::Pub(PubImmutableData::new(pub_idata_content)));
let data = app.client.get_sdata(pub_sdata_addr).await?;
assert_eq!(*data.address(), pub_sdata_addr);
Ok(())
}
#[tokio::test]
async fn account_info() -> Result<(), AppError> {
let mut app_auth_req = create_random_auth_req();
app_auth_req.app_permissions = AppPermissions {
transfer_coins: false,
perform_mutations: true,
get_balance: true,
};
let app = create_app_by_req(&app_auth_req).await?;
let client = app.client;
let orig_balance: Coins = client.get_balance(None).await?;
client
.put_idata(PubImmutableData::new(vec![1, 2, 3]))
.await?;
let new_balance: Coins = client.get_balance(None).await?;
assert_eq!(
new_balance,
orig_balance
.checked_sub(COST_OF_PUT)
.ok_or_else(|| AppError::Unexpected("failed to substract cost of put".to_string()))?
);
Ok(())
}