Lines
63.16 %
Functions
17.42 %
Branches
100 %
pub mod user {
use crate::db::DBError;
use crate::error::ServerError;
use crate::user::User;
use finance::{
account::{Account, AccountBuilder},
tag::Tag,
};
use sqlx::types::Uuid;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
impl User {
pub async fn add_account(&self, script: &[u8]) -> Result<Account, ServerError> {
let a = AccountBuilder::new().id(Uuid::new_v4()).build()?;
let mut tags = self.get_account_tags(&a).await?;
let tagdb: Arc<Mutex<HashMap<String, String>>> = Arc::new(Mutex::new(HashMap::new()));
{
let mut tagdb_lock = tagdb.lock().map_err(|err| {
log::error!("{}", t!("Mutex error: %{err}", err = err : {:?}));
ServerError::Lock
})?;
for t in &tags {
tagdb_lock.insert(t.tag_name.clone(), t.tag_value.clone());
}
let account = if script.is_empty() {
a
} else {
// TODO: Implement account hook similar to commodity
// Update the tags with the new values from `tagdb`
let tagdb_lock = tagdb.lock().map_err(|err| {
for t in &mut tags {
if let Some(value) = tagdb_lock.get(&t.tag_name) {
t.tag_value = value.to_owned();
// Call `update_account_tags` to apply the updates to the database
self.update_account_tags(&account, &tags).await?;
Ok(account)
pub async fn create_account(
&self,
name: &str,
parent: Option<Uuid>,
) -> Result<Account, ServerError> {
let a = Account {
id: Uuid::new_v4(),
parent,
let mut conn = self.get_connection().await.map_err(|err| {
log::error!("{}", t!("Database error: %{err}", err = err : {:?}));
ServerError::DB(err)
a.commit(&mut *conn).await?;
let tags: Vec<Tag> = vec![Tag {
tag_name: "name".to_string(),
tag_value: name.to_string(),
description: None,
}];
self.update_account_tags(&a, &tags).await?;
Ok(a)
pub async fn update_account_tags(
a: &Account,
tags: &[Tag],
) -> Result<(), ServerError> {
let tag_ids: Vec<Uuid> = tags.iter().map(|t| t.id).collect();
let names: Vec<String> = tags.iter().map(|t| t.tag_name.clone()).collect();
let values: Vec<String> = tags.iter().map(|t| t.tag_value.clone()).collect();
let _ = sqlx::query_file!(
"sql/update/accounts/tags.sql",
&a.id,
&tag_ids,
&names,
&values,
)
.fetch_one(&mut *conn)
.await
.map_err(|err| {
ServerError::DB(DBError::Sqlx(err))
Ok(())
pub async fn set_account_tag(&self, a: &Account, t: &Tag) -> Result<(), ServerError> {
"sql/set/accounts/tag.sql",
a.parent,
&t.id,
&t.tag_name,
&t.tag_value,
t.description
pub async fn get_account_tags(&self, a: &Account) -> Result<Vec<Tag>, ServerError> {
let tags = sqlx::query_file_as!(Tag, "sql/select/accounts/tags.sql", &a.id)
.fetch_all(&mut *conn)
log::error!("Database error: {err:?}");
Ok(tags)
pub async fn get_account_tag(&self, a: &Account, tag: &String) -> Result<Tag, ServerError> {
let tag = sqlx::query_file_as!(Tag, "sql/select/accounts/tag.sql", &a.id, tag)
Ok(tag)
#[cfg(test)]
mod account_tests {
use super::*;
use crate::db::DB_POOL;
#[cfg(feature = "testlog")]
use env_logger;
use finance::commodity::Commodity;
use log;
use sqlx::PgPool;
use supp_macro::local_db_sqlx_test;
use tokio::sync::OnceCell;
/// Context for keeping environment intact
static CONTEXT: OnceCell<()> = OnceCell::const_new();
static COMMODITY: OnceCell<Commodity> = OnceCell::const_new();
static USER: OnceCell<User> = OnceCell::const_new();
async fn setup() {
CONTEXT
.get_or_init(|| async {
let _ = env_logger::builder()
.is_test(true)
.filter_level(log::LevelFilter::Trace)
.try_init();
})
.await;
COMMODITY
Commodity {
fraction: 1000,
USER.get_or_init(|| async { User { id: Uuid::new_v4() } })
// #[local_db_sqlx_test]
// async fn test_add_account(pool: PgPool) -> Result<(), anyhow::Error> {
// let mut conn = pool.acquire().await.unwrap();
// USER.get()
// .unwrap()
// .commit()
// .await
// .expect("Failed to commit user to database");
// COMMODITY.get().unwrap().commit(&mut *conn).await?;
// let account = Account {
// id: Uuid::new_v4(),
// commodity_id: COMMODITY.get().unwrap().id,
// parent: None,
// };
// account.commit(&mut *conn).await?;
// let script = vec![];
// let user = USER.get().unwrap();
// let acc = user.add_account(&script).await?;
// assert!(!acc.commodity_id.is_nil());
// }
#[local_db_sqlx_test]
async fn test_create_account(pool: PgPool) -> Result<(), anyhow::Error> {
USER.get()
.unwrap()
.commit()
.expect("Failed to commit user to database");
let mut conn = pool.acquire().await.unwrap();
COMMODITY.get().unwrap().commit(&mut *conn).await?;
let user = USER.get().unwrap();
let a = user.create_account("Test Account", None).await?;
let tag = user.get_account_tag(&a, &"name".to_string()).await?;
assert_eq!(tag.tag_value, "Test Account");
async fn test_account_tag(pool: PgPool) -> Result<(), anyhow::Error> {
let account = Account {
parent: None,
account.commit(&mut *conn).await?;
user.set_account_tag(
&account,
&Tag::builder()
.id(Uuid::new_v4())
.tag_name("test")
.tag_value("testval")
.build()?,
.await?;
let tags = user.get_account_tags(&account).await?;
assert_eq!(tags.len(), 1);
assert_eq!(tags[0].tag_name, "test");
assert_eq!(tags[0].tag_value, "testval");
.tag_value("testval2")
assert_eq!(tags[0].tag_value, "testval2");
async fn test_get_account_tags(pool: PgPool) -> Result<(), anyhow::Error> {
.tag_name("test1")
.tag_value("value1")
.tag_name("test2")
.tag_value("value2")
let mut newtags = user.get_account_tags(&account).await?;
assert_eq!(newtags.len(), 2);
newtags[0].tag_value = "newvalue1".to_string();
user.update_account_tags(&account, &newtags).await?;
let updated_tags = user.get_account_tags(&account).await?;
assert_eq!(updated_tags[0].tag_value, "newvalue1");