Fixed unit tests.

This commit is contained in:
didier amyot committed 2023-04-13 08:30:47 -04:00
1 parent 1f05ae1957
commit 80153dd6ad
3 files changed
+97 -45

No files matched your search

+1
View File
@@ -3,3 +3,4 @@
/*.sqlite /*.sqlite
/result /result
/.idea /.idea
.tmp
+3 -1
View File
@@ -41,7 +41,9 @@
pre-commit pre-commit
rustPackages.clippy rustPackages.clippy
cargo-tarpaulin cargo-tarpaulin
deno]; deno
foreman
postgresql];
RUST_SRC_PATH = rustPlatform.rustLibSrc; RUST_SRC_PATH = rustPlatform.rustLibSrc;
}; };
}); });
+93 -44
View File
@@ -1,12 +1,12 @@
use fraction::Fraction; use fraction::Fraction;
use rusqlite::{params, Connection, OptionalExtension, Result, Row}; use rusqlite::{params, Connection, ErrorCode, OptionalExtension, Result, Row};
use crate::db::_external_id::{AccountId, Id, UserId}; use crate::db::_external_id::{AccountId, Id, UserId};
use crate::db::_securities::Security; use crate::db::_securities::Security;
use crate::db::external_id::SecurityId; use crate::db::external_id::SecurityId;
use crate::util::{CustomResult, Error};
use crate::util::Error::FuckingError; use crate::util::Error::FuckingError;
use crate::util::{CustomResult, Error};
#[derive(Debug)] #[derive(Debug)]
pub enum AccountType { pub enum AccountType {
@@ -15,19 +15,18 @@ pub enum AccountType {
} }
impl AccountType { impl AccountType {
fn from_string(s: &String) -> Option<AccountType> {
fn from_string(s: & String) -> Option<AccountType>{
if s == "security_emitter" { if s == "security_emitter" {
Some(AccountType::SecurityEmitter) Some(AccountType::SecurityEmitter)
} else if s == "checking"{ } else if s == "checking" {
Some(AccountType::Checking) Some(AccountType::Checking)
}else{ } else {
None None
} }
} }
pub fn from_row(row: &Row) -> Result<AccountType> { pub fn from_row(row: &Row) -> Result<AccountType> {
let s: String = row.get("acc_account_type")?; let s: String = row.get("acc_account_type")?;
AccountType::from_string(& s).ok_or(rusqlite::Error::ExecuteReturnedResults) AccountType::from_string(&s).ok_or(rusqlite::Error::ExecuteReturnedResults)
} }
} }
@@ -41,12 +40,13 @@ impl PartialEq for AccountType {
} }
} }
impl ToString for AccountType{ impl ToString for AccountType {
fn to_string(&self) -> String { fn to_string(&self) -> String {
match self { match self {
AccountType::Checking => "checking", AccountType::Checking => "checking",
AccountType::SecurityEmitter => "security_emitter", AccountType::SecurityEmitter => "security_emitter",
}.to_string() }
.to_string()
} }
} }
@@ -107,9 +107,9 @@ impl Account {
} }
} }
impl Clone for Account{ impl Clone for Account {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Account{ Account {
id: self.id, id: self.id,
amount: self.amount, amount: self.amount,
timestamp: self.timestamp, timestamp: self.timestamp,
@@ -117,7 +117,6 @@ impl Clone for Account{
account_type: self.account_type.clone(), account_type: self.account_type.clone(),
label: self.label.clone(), label: self.label.clone(),
} }
} }
} }
#[derive(Debug)] #[derive(Debug)]
@@ -192,7 +191,7 @@ pub fn list_accounts<'a>(conn: &Connection) -> CustomResult<Vec<Account>> {
return Ok(ret); return Ok(ret);
} }
pub fn list_user_accounts(conn: &Connection, user_id: & UserId) -> CustomResult<Vec<Account>> { pub fn list_user_accounts(conn: &Connection, user_id: &UserId) -> CustomResult<Vec<Account>> {
let q = "SELECT acc.id AS acc_id, let q = "SELECT acc.id AS acc_id,
acc.amount AS acc_amount, acc.amount AS acc_amount,
UNIXEPOCH(acc.timestamp) AS acc_timestamp, UNIXEPOCH(acc.timestamp) AS acc_timestamp,
@@ -237,7 +236,10 @@ pub fn get_account_by_id(tx: &Connection, id: &AccountId) -> CustomResult<Accoun
.map_err(Error::SqlError); .map_err(Error::SqlError);
} }
pub fn get_checking_accounts_for_sec_id(tx: &Connection, sec_id: &SecurityId) -> CustomResult<Vec<Account>> { pub fn get_checking_accounts_for_sec_id(
tx: &Connection,
sec_id: &SecurityId,
) -> CustomResult<Vec<Account>> {
let q = " let q = "
SELECT acc.id AS acc_id, SELECT acc.id AS acc_id,
acc.amount AS acc_amount, acc.amount AS acc_amount,
@@ -260,7 +262,6 @@ pub fn get_checking_accounts_for_sec_id(tx: &Connection, sec_id: &SecurityId) ->
.map(Result::unwrap) .map(Result::unwrap)
.collect(); .collect();
return Ok(ret); return Ok(ret);
} }
fn create_account( fn create_account(
@@ -353,7 +354,7 @@ impl AccountChangeReceipt {
amount: row.get("amount")?, amount: row.get("amount")?,
timestamp: row.get("timestamp")?, timestamp: row.get("timestamp")?,
security_id: SecurityId::from_id(row.get("security_id")?), security_id: SecurityId::from_id(row.get("security_id")?),
account_type: AccountType::from_row(row)? account_type: AccountType::from_row(row)?,
}) })
} }
} }
@@ -373,6 +374,13 @@ fn update_account_history(tx: &Connection, account_id: &AccountId) -> CustomResu
Ok(()) Ok(())
} }
fn is_invalid_amount_error(e: &rusqlite::Error) -> bool {
match e {
rusqlite::Error::SqliteFailure(uf, _) => uf.code == ErrorCode::ConstraintViolation,
_ => false,
}
}
/// Super low level functions that needs to be wrapped in something else. /// Super low level functions that needs to be wrapped in something else.
/// ///
/// This method does NOT guarantee that all amount added into an account /// This method does NOT guarantee that all amount added into an account
@@ -398,16 +406,23 @@ pub fn add_to_account(
security AS security_id security AS security_id
"; ";
let params = params![amount, account_id]; let params = params![amount, account_id];
let ret = tx let res = tx.query_row(q, params, AccountChangeReceipt::from_row);
.query_row(q, params, AccountChangeReceipt::from_row) let ret = res.map_err(|e| {
.map_err(Error::SqlError); if is_invalid_amount_error(&e) {
Error::InvalidAmount(format!("Invalid value {:} added to account {:}", amount, account_id))
} else {
Error::SqlError(e)
}
})?;
update_account_history(tx, account_id)?; update_account_history(tx, account_id)?;
return ret; return Ok(ret);
} }
pub fn get_number_of_accounts(tx: &Connection, pub fn get_number_of_accounts(
sec: &SecurityId, tx: &Connection,
account_type: AccountType) -> CustomResult<i64>{ sec: &SecurityId,
account_type: AccountType,
) -> CustomResult<i64> {
let q = " let q = "
SELECT COUNT(*) AS count SELECT COUNT(*) AS count
FROM accounts FROM accounts
@@ -415,15 +430,15 @@ pub fn get_number_of_accounts(tx: &Connection,
AND account_type = ? AND account_type = ?
"; ";
let params = params![sec, account_type.to_string()]; let params = params![sec, account_type.to_string()];
tx.query_row(q, params, |row| row.get("count")).map_err(Error::SqlError) tx.query_row(q, params, |row| row.get("count"))
.map_err(Error::SqlError)
} }
/// Market cap is computer by summing the security emitter accounts. /// Market cap is computer by summing the security emitter accounts.
/// We use this technique because the sum of the security /// We use this technique because the sum of the security
/// emitter accounts MUST equals (more or less very tiny interest /// emitter accounts MUST equals (more or less very tiny interest
/// amounts) the sum of the checking accounts. /// amounts) the sum of the checking accounts.
pub fn get_security_market_cap(tx: &Connection, pub fn get_security_market_cap(tx: &Connection, sec: &SecurityId) -> CustomResult<i64> {
sec: &SecurityId) -> CustomResult<i64> {
let q = " let q = "
SELECT COALESCE(-1 * SUM(amount), 0) AS count SELECT COALESCE(-1 * SUM(amount), 0) AS count
FROM accounts FROM accounts
@@ -431,7 +446,8 @@ pub fn get_security_market_cap(tx: &Connection,
AND account_type = ? AND account_type = ?
"; ";
let params = params![sec, AccountType::SecurityEmitter.to_string()]; let params = params![sec, AccountType::SecurityEmitter.to_string()];
tx.query_row(q, params, |row| row.get("count")).map_err(Error::SqlError) tx.query_row(q, params, |row| row.get("count"))
.map_err(Error::SqlError)
} }
#[cfg(test)] #[cfg(test)]
@@ -472,14 +488,13 @@ mod test_accounts {
return Ok(ret); return Ok(ret);
} }
fn assert_test_account_type_to_string_and_from_row_works(account_type: AccountType){ fn assert_test_account_type_to_string_and_from_row_works(account_type: AccountType) {
let serialized = account_type.to_string(); let serialized = account_type.to_string();
assert_eq!(AccountType::from_string(&serialized).unwrap(), account_type); assert_eq!(AccountType::from_string(&serialized).unwrap(), account_type);
} }
#[test] #[test]
fn test_account_type_to_string_and_from_row(){ fn test_account_type_to_string_and_from_row() {
assert_test_account_type_to_string_and_from_row_works(AccountType::Checking); assert_test_account_type_to_string_and_from_row_works(AccountType::Checking);
assert_test_account_type_to_string_and_from_row_works(AccountType::SecurityEmitter); assert_test_account_type_to_string_and_from_row_works(AccountType::SecurityEmitter);
} }
@@ -492,10 +507,12 @@ mod test_accounts {
let sec = secs.get(0).unwrap(); let sec = secs.get(0).unwrap();
let tx = conn.transaction().unwrap(); let tx = conn.transaction().unwrap();
let acc = create_checking_account(&tx, let acc = create_checking_account(
&sec.id(), &tx,
&user.id(), &sec.id(),
&format!("Account for {:} in {:}", sec.id(), user.id()))?; &user.id(),
&format!("Account for {:} in {:}", sec.id(), user.id()),
)?;
assert_eq!(acc.account_type, AccountType::Checking); assert_eq!(acc.account_type, AccountType::Checking);
assert_eq!(0, acc.raw_amount()); assert_eq!(0, acc.raw_amount());
@@ -567,7 +584,7 @@ mod test_accounts {
let tx = conn.transaction().unwrap(); let tx = conn.transaction().unwrap();
let sec = secs.get(0).unwrap().id(); let sec = secs.get(0).unwrap().id();
let user = users.get(0).unwrap().id(); let user = users.get(0).unwrap().id();
let acc = create_checking_account(&tx, &sec, &user, & "acc".to_string()).unwrap(); let acc = create_checking_account(&tx, &sec, &user, &"acc".to_string()).unwrap();
let receipt = add_to_account(&tx, &acc.id(), 33)?; let receipt = add_to_account(&tx, &acc.id(), 33)?;
assert_eq!(receipt.account_id, acc.id()); assert_eq!(receipt.account_id, acc.id());
assert_eq!(receipt.amount, 33); assert_eq!(receipt.amount, 33);
@@ -591,7 +608,7 @@ mod test_accounts {
let tx = conn.transaction().unwrap(); let tx = conn.transaction().unwrap();
let sec = secs.get(0).unwrap().id(); let sec = secs.get(0).unwrap().id();
let user = users.get(0).unwrap().id(); let user = users.get(0).unwrap().id();
let acc = create_security_emitter_account(&tx, &sec, &user, & "acc".to_string())?; let acc = create_security_emitter_account(&tx, &sec, &user, &"acc".to_string())?;
let receipt = add_to_account(&tx, &acc.id(), -33)?; let receipt = add_to_account(&tx, &acc.id(), -33)?;
assert_eq!(receipt.account_id, acc.id()); assert_eq!(receipt.account_id, acc.id());
@@ -608,16 +625,30 @@ mod test_accounts {
Ok(()) Ok(())
} }
#[test]
fn test_add_to_account_unfounded_err() -> CustomResult<()> {
let (mut conn, users, secs) = setup();
let sec = secs.get(0).unwrap().id();
let user = users.get(0).unwrap().id();
// let sec_emitter_acc = create_security_emitter_account(&conn, &sec, &user, & "sec emitter acc".to_string())?;
let checking_account =
create_checking_account(&conn, &sec, &user, &"checking acc".to_string())?;
match add_to_account(&conn, &checking_account.id(), -33) {
Err(Error::InvalidAmount(_)) => Ok(()),
_ => Err(Error::Testing("Expected InvalidAmount error".to_string()))
}
}
#[test] #[test]
fn test_list_user_accounts() -> CustomResult<()> { fn test_list_user_accounts() -> CustomResult<()> {
let (mut conn, users, secs) = setup(); let (mut conn, users, secs) = setup();
{ {
let tx = conn.transaction().unwrap(); let tx = conn.transaction().unwrap();
create_all_accounts(& tx, &users, &secs)?; create_all_accounts(&tx, &users, &secs)?;
tx.commit().map_err(Error::SqlError)?; tx.commit().map_err(Error::SqlError)?;
} }
let res = list_user_accounts(& conn, & users[0].id())?; let res = list_user_accounts(&conn, &users[0].id())?;
assert_eq!(res.len(), 2); assert_eq!(res.len(), 2);
Ok(()) Ok(())
} }
@@ -625,15 +656,28 @@ mod test_accounts {
#[test] #[test]
fn test_get_number_of_accounts() -> CustomResult<()> { fn test_get_number_of_accounts() -> CustomResult<()> {
let (mut conn, users, secs) = setup(); let (mut conn, users, secs) = setup();
create_all_accounts(&conn, &users, &secs)?; create_all_accounts(&conn, &users, &secs)?;
let n = get_number_of_accounts(&conn, &secs.get(0).unwrap().id(), AccountType::Checking)?; let n = get_number_of_accounts(&conn, &secs.get(0).unwrap().id(), AccountType::Checking)?;
assert_eq!(n, 2); assert_eq!(n, 2);
let n = get_number_of_accounts(&conn, &secs.get(0).unwrap().id(), AccountType::SecurityEmitter)?; let n = get_number_of_accounts(
&conn,
&secs.get(0).unwrap().id(),
AccountType::SecurityEmitter,
)?;
assert_eq!(n, 0); assert_eq!(n, 0);
create_security_emitter_account(&conn, &secs.get(0).unwrap().id(), &users.get(0).unwrap().id(), &"".to_string())?; create_security_emitter_account(
let n = get_number_of_accounts(&conn, &secs.get(0).unwrap().id(), AccountType::SecurityEmitter)?; &conn,
&secs.get(0).unwrap().id(),
&users.get(0).unwrap().id(),
&"".to_string(),
)?;
let n = get_number_of_accounts(
&conn,
&secs.get(0).unwrap().id(),
AccountType::SecurityEmitter,
)?;
assert_eq!(n, 1); assert_eq!(n, 1);
Ok(()) Ok(())
} }
@@ -641,9 +685,14 @@ mod test_accounts {
#[test] #[test]
fn test_get_security_market_cap() -> CustomResult<()> { fn test_get_security_market_cap() -> CustomResult<()> {
let (mut conn, users, secs) = setup(); let (mut conn, users, secs) = setup();
let accounts = create_all_accounts(&conn, &users, &secs.get(0..1).unwrap().to_vec())?; let accounts = create_all_accounts(&conn, &users, &secs.get(0..1).unwrap().to_vec())?;
let sec_id = &secs.get(0).unwrap().id(); let sec_id = &secs.get(0).unwrap().id();
let sec_emitter = create_security_emitter_account(&conn, sec_id, &users.get(0).unwrap().id(), &"".to_string())?; let sec_emitter = create_security_emitter_account(
&conn,
sec_id,
&users.get(0).unwrap().id(),
&"".to_string(),
)?;
let n = get_security_market_cap(&conn, sec_id)?; let n = get_security_market_cap(&conn, sec_id)?;
assert_eq!(n, 0); assert_eq!(n, 0);