mod common;
use common::*;
use futures::future::{result, Either};
use interledger_api::{AccountSettings, NodeStore};
use interledger_btp::{BtpAccount, BtpStore};
use interledger_ccp::{CcpRoutingAccount, RoutingRelation};
use interledger_http::{HttpAccount, HttpStore};
use interledger_packet::Address;
use interledger_service::Account as AccountTrait;
use interledger_service::{AccountStore, AddressStore, Username};
use interledger_service_util::BalanceStore;
use interledger_store_redis::AccountId;
use log::{debug, error};
use redis::Client;
use secrecy::SecretString;
use std::str::FromStr;
#[test]
fn picks_up_parent_during_initialization() {
let context = TestContext::new();
block_on(
result(Client::open(context.get_client_connection_info()))
.map_err(|err| error!("Error creating Redis client: {:?}", err))
.and_then(|client| {
debug!("Connected to redis: {:?}", client);
client
.get_shared_async_connection()
.map_err(|err| error!("Error connecting to Redis: {:?}", err))
})
.and_then(move |connection| {
redis::cmd("SET")
.arg("parent_node_account_address")
.arg("example.bob.node")
.query_async(connection)
.map_err(|err| panic!(err))
.and_then(move |(_, _): (_, redis::Value)| {
RedisStoreBuilder::new(context.get_client_connection_info(), [0; 32])
.connect()
.and_then(move |store| {
assert_eq!(
*store.ilp_address.read(),
Address::from_str("example.bob.node").unwrap()
);
let _ = context;
Ok(())
})
})
}),
)
.unwrap();
}
#[test]
fn insert_accounts() {
block_on(test_store().and_then(|(store, context, _accs)| {
store
.insert_account(ACCOUNT_DETAILS_2.clone())
.and_then(move |account| {
assert_eq!(
*account.ilp_address(),
Address::from_str("example.alice.user1.charlie").unwrap()
);
let _ = context;
Ok(())
})
}))
.unwrap();
}
#[test]
fn update_ilp_and_children_addresses() {
block_on(test_store().and_then(|(store, context, accs)| {
store
.insert_account(ACCOUNT_DETAILS_2.clone())
.and_then(move |acc2| {
let mut accs = accs.clone();
accs.push(acc2);
accs.sort_by_key(|a| a.username().clone());
let ilp_address = Address::from_str("test.parent.our_address").unwrap();
store
.set_ilp_address(ilp_address.clone())
.and_then(move |_| {
let ret = store.get_ilp_address();
assert_eq!(ilp_address, ret);
store.get_all_accounts().and_then(move |accounts: Vec<_>| {
let mut accounts = accounts.clone();
accounts.sort_by(|a, b| {
a.username()
.as_bytes()
.partial_cmp(b.username().as_bytes())
.unwrap()
});
for (a, b) in accounts.into_iter().zip(&accs) {
if a.routing_relation() == RoutingRelation::Child
|| a.routing_relation() == RoutingRelation::NonRoutingAccount
{
assert_eq!(
*a.ilp_address(),
ilp_address.with_suffix(a.username().as_bytes()).unwrap()
);
} else {
assert_eq!(a.ilp_address(), b.ilp_address());
}
}
let _ = context;
Ok(())
})
})
})
}))
.unwrap();
}
#[test]
fn only_one_parent_allowed() {
let mut acc = ACCOUNT_DETAILS_2.clone();
acc.routing_relation = Some("Parent".to_owned());
acc.username = Username::from_str("another_name").unwrap();
acc.ilp_address = Some(Address::from_str("example.another_name").unwrap());
block_on(test_store().and_then(|(store, context, accs)| {
store.insert_account(acc.clone()).then(move |res| {
assert!(res.is_err());
futures::future::join_all(vec![
Either::A(store.delete_account(accs[0].id()).and_then(|_| Ok(()))),
Either::B(store.clear_ilp_address()),
])
.and_then(move |_| {
store.insert_account(acc).and_then(move |_| {
let _ = context;
Ok(())
})
})
})
}))
.unwrap();
}
#[test]
fn delete_accounts() {
block_on(test_store().and_then(|(store, context, _accs)| {
store.get_all_accounts().and_then(move |accounts| {
let id = accounts[0].id();
store.delete_account(id).and_then(move |_| {
store.get_all_accounts().and_then(move |accounts| {
for a in accounts {
assert_ne!(id, a.id());
}
let _ = context;
Ok(())
})
})
})
}))
.unwrap();
}
#[test]
fn update_accounts() {
block_on(test_store().and_then(|(store, context, accounts)| {
context
.async_connection()
.map_err(|err| panic!(err))
.and_then(move |connection| {
let id = accounts[0].id();
redis::cmd("HMSET")
.arg(format!("accounts:{}", id))
.arg("balance")
.arg(600)
.arg("prepaid_amount")
.arg(400)
.query_async(connection)
.map_err(|err| panic!(err))
.and_then(move |(_, _): (_, redis::Value)| {
let mut new = ACCOUNT_DETAILS_0.clone();
new.asset_code = String::from("TUV");
store.update_account(id, new).and_then(move |account| {
assert_eq!(account.asset_code(), "TUV");
store.get_balance(account).and_then(move |balance| {
assert_eq!(balance, 1000);
let _ = context;
Ok(())
})
})
})
})
}))
.unwrap();
}
#[test]
fn modify_account_settings_settle_to_overflow() {
block_on(test_store().and_then(|(store, context, accounts)| {
let mut settings = AccountSettings::default();
settings.settle_to = Some(std::i64::MAX as u64 + 1);
let account = accounts[0].clone();
let id = account.id();
store
.modify_account_settings(id, settings)
.then(move |ret| {
assert!(ret.is_err());
let _ = context;
Ok(())
})
}))
.unwrap();
}
use std::default::Default;
#[test]
fn modify_account_settings_unchanged() {
block_on(test_store().and_then(|(store, context, accounts)| {
let settings = AccountSettings::default();
let account = accounts[0].clone();
let id = account.id();
store
.modify_account_settings(id, settings)
.and_then(move |ret| {
assert_eq!(
account.get_http_auth_token().unwrap(),
ret.get_http_auth_token().unwrap()
);
assert_eq!(
account.get_ilp_over_btp_outgoing_token().unwrap(),
ret.get_ilp_over_btp_outgoing_token().unwrap()
);
let _ = context;
Ok(())
})
}))
.unwrap();
}
#[test]
fn modify_account_settings() {
block_on(test_store().and_then(|(store, context, accounts)| {
let settings = AccountSettings {
ilp_over_http_outgoing_token: Some(SecretString::new("dylan:test_token".to_owned())),
ilp_over_http_incoming_token: Some(SecretString::new("http_in_new".to_owned())),
ilp_over_btp_outgoing_token: Some(SecretString::new("dylan:test".to_owned())),
ilp_over_btp_incoming_token: Some(SecretString::new("btp_in_new".to_owned())),
ilp_over_http_url: Some("http://example.com".to_owned()),
ilp_over_btp_url: Some("http://example.com".to_owned()),
settle_threshold: Some(-50),
settle_to: Some(100),
};
let account = accounts[0].clone();
let id = account.id();
store
.modify_account_settings(id, settings)
.and_then(move |ret| {
assert_eq!(ret.get_http_auth_token().unwrap(), "dylan:test_token",);
assert_eq!(
ret.get_ilp_over_btp_outgoing_token().unwrap(),
&b"dylan:test"[..],
);
let _ = context;
Ok(())
})
}))
.unwrap();
}
#[test]
fn starts_with_zero_balance() {
block_on(test_store().and_then(|(store, context, accs)| {
let account0 = accs[0].clone();
store.get_balance(account0).and_then(move |balance| {
assert_eq!(balance, 0);
let _ = context;
Ok(())
})
}))
.unwrap();
}
#[test]
fn fetches_account_from_username() {
block_on(test_store().and_then(|(store, context, accs)| {
store
.get_account_id_from_username(&Username::from_str("alice").unwrap())
.and_then(move |account_id| {
assert_eq!(account_id, accs[0].id());
let _ = context;
Ok(())
})
}))
.unwrap();
}
#[test]
fn duplicate_http_incoming_auth_works() {
let mut duplicate = ACCOUNT_DETAILS_2.clone();
duplicate.ilp_over_http_incoming_token =
Some(SecretString::new("incoming_auth_token".to_string()));
block_on(test_store().and_then(|(store, context, accs)| {
let original = accs[0].clone();
let original_id = original.id();
store.insert_account(duplicate).and_then(move |duplicate| {
let duplicate_id = duplicate.id();
assert_ne!(original_id, duplicate_id);
futures::future::join_all(vec![
store.get_account_from_http_auth(
&Username::from_str("alice").unwrap(),
"incoming_auth_token",
),
store.get_account_from_http_auth(
&Username::from_str("charlie").unwrap(),
"incoming_auth_token",
),
])
.and_then(move |accs| {
assert_ne!(accs[0].id(), accs[1].id());
assert_eq!(accs[0].id(), original_id);
assert_eq!(accs[1].id(), duplicate_id);
let _ = context;
Ok(())
})
})
}))
.unwrap();
}
#[test]
fn gets_account_from_btp_auth() {
block_on(test_store().and_then(|(store, context, accs)| {
store
.get_account_from_btp_auth(&Username::from_str("alice").unwrap(), "btp_token")
.and_then(move |acc| {
assert_eq!(acc.id(), accs[0].id());
let _ = context;
Ok(())
})
}))
.unwrap();
}
#[test]
fn gets_account_from_http_auth() {
block_on(test_store().and_then(|(store, context, accs)| {
store
.get_account_from_http_auth(
&Username::from_str("alice").unwrap(),
"incoming_auth_token",
)
.and_then(move |acc| {
assert_eq!(acc.id(), accs[0].id());
let _ = context;
Ok(())
})
}))
.unwrap();
}
#[test]
fn duplicate_btp_incoming_auth_works() {
let mut charlie = ACCOUNT_DETAILS_2.clone();
charlie.ilp_over_btp_incoming_token = Some(SecretString::new("btp_token".to_string()));
block_on(test_store().and_then(|(store, context, accs)| {
let alice = accs[0].clone();
let alice_id = alice.id();
store.insert_account(charlie).and_then(move |charlie| {
let charlie_id = charlie.id();
assert_ne!(alice_id, charlie_id);
futures::future::join_all(vec![
store.get_account_from_btp_auth(&Username::from_str("alice").unwrap(), "btp_token"),
store.get_account_from_btp_auth(
&Username::from_str("charlie").unwrap(),
"btp_token",
),
])
.and_then(move |accs| {
assert_ne!(accs[0].id(), accs[1].id());
assert_eq!(accs[0].id(), alice_id);
assert_eq!(accs[1].id(), charlie_id);
let _ = context;
Ok(())
})
})
}))
.unwrap();
}
#[test]
fn get_all_accounts() {
block_on(test_store().and_then(|(store, context, _accs)| {
store.get_all_accounts().and_then(move |accounts| {
assert_eq!(accounts.len(), 2);
let _ = context;
Ok(())
})
}))
.unwrap();
}
#[test]
fn gets_single_account() {
block_on(test_store().and_then(|(store, context, accs)| {
let store_clone = store.clone();
let acc = accs[0].clone();
store_clone
.get_accounts(vec![acc.id()])
.and_then(move |accounts| {
assert_eq!(accounts[0].ilp_address(), acc.ilp_address());
let _ = context;
Ok(())
})
}))
.unwrap();
}
#[test]
fn gets_multiple() {
block_on(test_store().and_then(|(store, context, accs)| {
let store_clone = store.clone();
let account_ids: Vec<AccountId> = accs.iter().rev().map(|a| a.id()).collect::<_>();
store_clone
.get_accounts(account_ids)
.and_then(move |accounts| {
assert_eq!(accounts[0].ilp_address(), accs[1].ilp_address());
assert_eq!(accounts[1].ilp_address(), accs[0].ilp_address());
let _ = context;
Ok(())
})
}))
.unwrap();
}
#[test]
fn decrypts_outgoing_tokens_acc() {
block_on(test_store().and_then(|(store, context, accs)| {
let acc = accs[0].clone();
store
.get_accounts(vec![acc.id()])
.and_then(move |accounts| {
let account = accounts[0].clone();
assert_eq!(
account.get_http_auth_token().unwrap(),
acc.get_http_auth_token().unwrap(),
);
assert_eq!(
account.get_ilp_over_btp_outgoing_token().unwrap(),
acc.get_ilp_over_btp_outgoing_token().unwrap(),
);
let _ = context;
Ok(())
})
}))
.unwrap()
}
#[test]
fn errors_for_unknown_accounts() {
let result = block_on(test_store().and_then(|(store, context, _accs)| {
store
.get_accounts(vec![AccountId::new(), AccountId::new()])
.then(move |result| {
let _ = context;
result
})
}));
assert!(result.is_err());
}