I should be working.
This commit is contained in:
1 parent
d262f0d2f8
commit
d78b4f1d90
5 files changed
+51
-41
No files matched your search
@@ -53,13 +53,11 @@ CREATE TABLE IF NOT EXISTS accounts
|
|||||||
security INTEGER NOT NULL,
|
security INTEGER NOT NULL,
|
||||||
account_type TEXT NOT NULL,
|
account_type TEXT NOT NULL,
|
||||||
|
|
||||||
owner INTEGER NOT NULL, -- todo: remove
|
|
||||||
amount INTEGER NOT NULL,
|
amount INTEGER NOT NULL,
|
||||||
timestamp DATETIME NOT NULL,
|
timestamp DATETIME NOT NULL,
|
||||||
creation_time DATETIME DEFAULT CURRENT_TIMESTAMP,
|
creation_time DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
|
||||||
FOREIGN KEY (security) REFERENCES securities (id),
|
FOREIGN KEY (security) REFERENCES securities (id),
|
||||||
FOREIGN KEY (owner) REFERENCES users (id),
|
|
||||||
FOREIGN KEY (account_type) REFERENCES account_types (name)
|
FOREIGN KEY (account_type) REFERENCES account_types (name)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -70,7 +68,7 @@ CREATE TABLE IF NOT EXISTS account_user_permissions
|
|||||||
user_id INTEGER NOT NULL,
|
user_id INTEGER NOT NULL,
|
||||||
creation_time DATETIME DEFAULT CURRENT_TIMESTAMP,
|
creation_time DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
|
||||||
FOREIGN KEY (account_id) REFERENCES account (id),
|
FOREIGN KEY (account_id) REFERENCES accounts (id),
|
||||||
FOREIGN KEY (user_id) REFERENCES users (id)
|
FOREIGN KEY (user_id) REFERENCES users (id)
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|||||||
+8
-4
@@ -88,14 +88,18 @@ pub fn get_account_by_id(tx: &Transaction, id: &AccountId) -> Result<Account> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn create_account(tx: &Transaction, sec: &SecurityId, user: &UserId, account_type: AccountType) -> Result<Account> {
|
fn create_account(tx: &Transaction, sec: &SecurityId, user: &UserId, account_type: AccountType) -> Result<Account> {
|
||||||
let q = "INSERT INTO accounts(security, owner, amount, timestamp, account_type)
|
let q = "INSERT INTO accounts(security, amount, timestamp, account_type)
|
||||||
VALUES (?1, ?2, 0, CURRENT_TIMESTAMP, ?3)";
|
VALUES (?1, 0, CURRENT_TIMESTAMP, ?2)";
|
||||||
let account_type = match account_type {
|
let account_type = match account_type {
|
||||||
AccountType::Checking => "checking",
|
AccountType::Checking => "checking",
|
||||||
AccountType::SecurityEmitter => "security_emitter",
|
AccountType::SecurityEmitter => "security_emitter",
|
||||||
};
|
};
|
||||||
tx.execute(q, (sec, user, account_type))?;
|
tx.execute(q, (sec, account_type))?;
|
||||||
let id = AccountId::from_id(tx.last_insert_rowid() as u32);
|
let id = AccountId::from_id(tx.last_insert_rowid() as u32);
|
||||||
|
|
||||||
|
let q = "INSERT INTO account_user_permissions(user_id, account_id)
|
||||||
|
VALUES(?1, ?2)";
|
||||||
|
tx.execute(q, params![user, id])?;
|
||||||
return get_account_by_id(tx, &id);
|
return get_account_by_id(tx, &id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,7 +112,7 @@ pub fn create_security_emitter_account(tx: &Transaction, sec: &SecurityId, user:
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_owner(tx: &Transaction, account_id: &AccountId, user_id: &UserId) -> Result<bool> {
|
pub fn is_owner(tx: &Transaction, account_id: &AccountId, user_id: &UserId) -> Result<bool> {
|
||||||
let q = "SELECT id FROM accounts WHERE id = ?1 AND owner = ?2";
|
let q = "SELECT id FROM account_user_permissions WHERE account_id = ?1 AND user_id = ?2";
|
||||||
let params = params![account_id, user_id];
|
let params = params![account_id, user_id];
|
||||||
let ite = |row: &Row| row.get::<usize, u32>(0);
|
let ite = |row: &Row| row.get::<usize, u32>(0);
|
||||||
let option = tx.query_row(q, params, ite).optional()?;
|
let option = tx.query_row(q, params, ite).optional()?;
|
||||||
|
|||||||
+10
-6
@@ -2,9 +2,13 @@ use rusqlite::{Result, Transaction};
|
|||||||
|
|
||||||
use crate::db::_external_id::{AccountId, SecurityId, UserId};
|
use crate::db::_external_id::{AccountId, SecurityId, UserId};
|
||||||
|
|
||||||
|
pub type Drawer = (UserId, AccountId, SecurityId);
|
||||||
|
pub type Payee = (UserId, SecurityId);
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SecurityEmitterCheque {
|
pub struct SecurityEmitterCheque {
|
||||||
pub drawer_user_id: UserId,
|
pub drawer_user_id: UserId,
|
||||||
|
pub drawer_account_id: AccountId,
|
||||||
pub security_id: SecurityId,
|
pub security_id: SecurityId,
|
||||||
pub payee_user_id: UserId,
|
pub payee_user_id: UserId,
|
||||||
pub cheque_number: u32,
|
pub cheque_number: u32,
|
||||||
@@ -12,15 +16,15 @@ pub struct SecurityEmitterCheque {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SecurityEmitterCheque {
|
impl SecurityEmitterCheque {
|
||||||
pub fn new(drawer_user_id: UserId,
|
pub fn new(drawer: Drawer,
|
||||||
security_id: SecurityId,
|
payee: Payee,
|
||||||
payee_user_id: UserId,
|
|
||||||
amount: u32,
|
amount: u32,
|
||||||
cheque_number: u32) -> SecurityEmitterCheque {
|
cheque_number: u32) -> SecurityEmitterCheque {
|
||||||
SecurityEmitterCheque {
|
SecurityEmitterCheque {
|
||||||
drawer_user_id,
|
drawer_user_id: drawer.0,
|
||||||
security_id,
|
drawer_account_id: drawer.1,
|
||||||
payee_user_id,
|
security_id: drawer.2,
|
||||||
|
payee_user_id: payee.0,
|
||||||
cheque_number,
|
cheque_number,
|
||||||
amount,
|
amount,
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-2
@@ -18,11 +18,15 @@ pub mod users {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub mod accounts {
|
pub mod accounts {
|
||||||
pub use super::_accounts::{Account, create_checking_account, get_account_by_id, list_accounts};
|
pub use super::_accounts::{Account,
|
||||||
|
create_checking_account,
|
||||||
|
create_security_emitter_account,
|
||||||
|
get_account_by_id,
|
||||||
|
list_accounts};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod cheques {
|
pub mod cheques {
|
||||||
pub use super::_cheques::SecurityEmitterCheque;
|
pub use super::_cheques::{SecurityEmitterCheque, Drawer, Payee};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod test_utils {
|
pub mod test_utils {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use rusqlite::{ffi, params, Params, Transaction};
|
use rusqlite::{ffi, params, Params, Savepoint, Transaction};
|
||||||
|
|
||||||
use crate::db::accounts::{Account, get_account_by_id};
|
use crate::db::accounts::{Account, get_account_by_id};
|
||||||
use crate::db::cheques::SecurityEmitterCheque;
|
use crate::db::cheques::SecurityEmitterCheque;
|
||||||
@@ -44,18 +44,20 @@ fn _add_cheque_to_cashed_cheques_log(tx: &TransactionHelper,
|
|||||||
fn cash_cheque(tx: &Transaction,
|
fn cash_cheque(tx: &Transaction,
|
||||||
cheque: &SecurityEmitterCheque,
|
cheque: &SecurityEmitterCheque,
|
||||||
account_id: &AccountId) -> CustomResult<()> {
|
account_id: &AccountId) -> CustomResult<()> {
|
||||||
|
// let mut savepoint: Savepoint = tx.savepoint_with_name("bib").map_err(Error::SqlError)?;
|
||||||
if !securities::is_owner(tx, &cheque.security_id, &cheque.drawer_user_id).map_err(Error::SqlError)? {
|
if !securities::is_owner(&tx, &cheque.security_id, &cheque.drawer_user_id).map_err(Error::SqlError)? {
|
||||||
return Err(Error::InvalidDrawer(format!("The drawer is the security owner: {:?}", cheque)));
|
return Err(Error::InvalidDrawer(format!("The drawer is the security owner: {:?}", cheque)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo:
|
// todo:
|
||||||
// 1. payee is owner of the account.
|
// 1. payee is owner of the account.
|
||||||
|
// 2. drawer is owner of the account.
|
||||||
|
|
||||||
|
|
||||||
let myx = TransactionHelper::new(tx);
|
let myx = TransactionHelper::new(tx);
|
||||||
_add_securities_to_account(&myx, cheque, account_id)?;
|
_add_securities_to_account(&myx, cheque, account_id)?;
|
||||||
_add_cheque_to_cashed_cheques_log(&myx, cheque, account_id)?;
|
_add_cheque_to_cashed_cheques_log(&myx, cheque, account_id)?;
|
||||||
|
// savepoint.rollback().map_err(Error::SqlError)?;
|
||||||
// create transaction log
|
// create transaction log
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -64,9 +66,10 @@ fn cash_cheque(tx: &Transaction,
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_caching_cheques {
|
mod test_caching_cheques {
|
||||||
use rusqlite::{Connection, params};
|
use rusqlite::{Connection, params};
|
||||||
use crate::db::accounts::create_checking_account;
|
use crate::db::accounts::{create_checking_account, create_security_emitter_account};
|
||||||
|
|
||||||
use crate::db::{accounts, test_utils};
|
use crate::db::{accounts, test_utils};
|
||||||
|
use crate::db::cheques::{Drawer, Payee};
|
||||||
use crate::db::external_id::{Id, SecurityId, UserId};
|
use crate::db::external_id::{Id, SecurityId, UserId};
|
||||||
use crate::db::users::User;
|
use crate::db::users::User;
|
||||||
use crate::migration;
|
use crate::migration;
|
||||||
@@ -79,34 +82,32 @@ mod test_caching_cheques {
|
|||||||
return conn;
|
return conn;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_basic() -> (Connection, UserId, SecurityId, UserId, AccountId) {
|
fn setup_basic() -> (Connection, Drawer, Payee, AccountId) {
|
||||||
let mut conn = setup_conn();
|
let mut conn = setup_conn();
|
||||||
let tx = conn.transaction().unwrap();
|
let tx = conn.transaction().unwrap();
|
||||||
let (users, securities, accounts) = test_utils::setup_basic_environment(&tx).unwrap();
|
let (users, securities, accounts) = test_utils::setup_basic_environment(&tx).unwrap();
|
||||||
let sec = securities.get(0).unwrap();
|
let sec = securities.get(0).unwrap();
|
||||||
let drawer = users.get(0).unwrap();
|
let drawer = users.get(0).unwrap();
|
||||||
let payee = users.get(1).unwrap();
|
let payee = users.get(1).unwrap();
|
||||||
|
let drawer_account = create_security_emitter_account(&tx, &sec.id(), &drawer.id()).unwrap();
|
||||||
let payee_account = create_checking_account(&tx, &sec.id(), &payee.id()).unwrap();
|
let payee_account = create_checking_account(&tx, &sec.id(), &payee.id()).unwrap();
|
||||||
|
|
||||||
tx.commit().unwrap();
|
tx.commit().unwrap();
|
||||||
(conn, drawer.id(), sec.id(), payee.id(), payee_account.id())
|
let drawer: Drawer = (drawer.id(), drawer_account.id(), sec.id());
|
||||||
|
let payee: Payee = (payee.id(), sec.id());
|
||||||
|
(conn, drawer, payee, payee_account.id())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_basic_happy_path() -> CustomResult<()> {
|
fn test_basic_happy_path() -> CustomResult<()> {
|
||||||
let (mut conn, drawer, sec, payee, payee_account) = setup_basic();
|
let (mut conn, drawer, payee, payee_account) = setup_basic();
|
||||||
let tx = conn.transaction().unwrap();
|
let mut tx = conn.transaction().unwrap();
|
||||||
|
|
||||||
let cheque = SecurityEmitterCheque::new(
|
let cheque = SecurityEmitterCheque::new(
|
||||||
drawer,
|
drawer, payee, 1000, 99);
|
||||||
sec,
|
|
||||||
payee,
|
|
||||||
1000,
|
|
||||||
99,
|
|
||||||
);
|
|
||||||
|
|
||||||
cash_cheque(&tx, &cheque, &payee_account)?;
|
cash_cheque(&mut tx, &cheque, &payee_account)?;
|
||||||
tx.commit().unwrap();
|
tx.commit().unwrap();
|
||||||
|
|
||||||
let tx = conn.transaction().unwrap();
|
let tx = conn.transaction().unwrap();
|
||||||
@@ -118,23 +119,22 @@ mod test_caching_cheques {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_double_spend() -> CustomResult<()> {
|
fn test_double_spend() -> CustomResult<()> {
|
||||||
let (mut conn, drawer, sec, payee, payee_account) = setup_basic();
|
let (mut conn, drawer, payee, payee_account) = setup_basic();
|
||||||
let amount: u32 = 1000;
|
let amount: u32 = 1000;
|
||||||
let tx = conn.transaction().unwrap();
|
let mut tx = conn.transaction().unwrap();
|
||||||
|
|
||||||
let cheque = SecurityEmitterCheque::new(
|
let cheque = SecurityEmitterCheque::new(
|
||||||
drawer,
|
drawer,
|
||||||
sec,
|
|
||||||
payee,
|
payee,
|
||||||
1000,
|
1000,
|
||||||
99,
|
99,
|
||||||
);
|
);
|
||||||
|
|
||||||
cash_cheque(&tx, &cheque, &payee_account)?;
|
cash_cheque(&mut tx, &cheque, &payee_account)?;
|
||||||
tx.commit().unwrap();
|
tx.commit().unwrap();
|
||||||
let tx = conn.transaction().unwrap();
|
let mut tx = conn.transaction().unwrap();
|
||||||
|
|
||||||
let ret = cash_cheque(&tx, &cheque, &payee_account);
|
let ret = cash_cheque(&mut tx, &cheque, &payee_account);
|
||||||
match ret {
|
match ret {
|
||||||
Err(Error::DoubleSpendingError(_)) => Ok(()),
|
Err(Error::DoubleSpendingError(_)) => Ok(()),
|
||||||
_ => Err(Error::Testing("Expected double spending error here.".to_string()))
|
_ => Err(Error::Testing("Expected double spending error here.".to_string()))
|
||||||
@@ -143,19 +143,19 @@ mod test_caching_cheques {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_drawer() -> CustomResult<()> {
|
fn test_invalid_drawer() -> CustomResult<()> {
|
||||||
let (mut conn, drawer, sec, payee, payee_account) = setup_basic();
|
let (mut conn, drawer, payee, payee_account) = setup_basic();
|
||||||
let amount: u32 = 1000;
|
let amount: u32 = 1000;
|
||||||
let tx = conn.transaction().unwrap();
|
let mut tx = conn.transaction().unwrap();
|
||||||
let not_currency_owner = UserId::from_id(drawer.get_id() + 72623);
|
let not_currency_owner_user_id = UserId::from_id(drawer.0.get_id() + 72623);
|
||||||
|
let not_currency_owner: Drawer = (not_currency_owner_user_id, drawer.1, drawer.2);
|
||||||
let cheque = SecurityEmitterCheque::new(
|
let cheque = SecurityEmitterCheque::new(
|
||||||
not_currency_owner,
|
not_currency_owner,
|
||||||
sec,
|
|
||||||
payee,
|
payee,
|
||||||
1000,
|
1000,
|
||||||
99,
|
99,
|
||||||
);
|
);
|
||||||
|
|
||||||
let ret = cash_cheque(&tx, &cheque, &payee_account);
|
let ret = cash_cheque(&mut tx, &cheque, &payee_account);
|
||||||
match ret {
|
match ret {
|
||||||
Err(Error::InvalidDrawer(_)) => Ok(()),
|
Err(Error::InvalidDrawer(_)) => Ok(()),
|
||||||
_ => Err(Error::Testing("Expected invalid drawer here.".to_string()))
|
_ => Err(Error::Testing("Expected invalid drawer here.".to_string()))
|
||||||
|
|||||||
Reference in new issue
Block a user