Lines
73.94 %
Functions
18.18 %
Branches
100 %
pub mod user {
use crate::db::DBError;
use crate::error::ServerError;
use crate::user::User;
use finance::{
commodity::{Commodity, CommodityBuilder},
tag::Tag,
};
use scripting::commodity::apply_commodity_hook;
use sqlx::types::Uuid;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
impl User {
pub async fn add_commodity(&self, script: &[u8]) -> Result<Commodity, ServerError> {
let c = CommodityBuilder::new()
.id(Uuid::new_v4())
.fraction(1)
.build()?;
let mut tags = self.get_commodity_tags(&c).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 commodity = if script.is_empty() {
c
} else {
apply_commodity_hook(c, tagdb.clone(), script).await?
// 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_commodity_tags` to apply the updates to the database
self.update_commodity_tags(&commodity, &tags).await?;
Ok(commodity)
pub async fn create_commodity(
&self,
fraction: i64,
symbol: String,
name: String,
) -> Result<Commodity, ServerError> {
let c = Commodity {
id: Uuid::new_v4(),
fraction,
let mut conn = self.get_connection().await.map_err(|err| {
log::error!("{}", t!("Database error: %{err}", err = err : {:?}));
ServerError::DB(err)
c.commit(&mut *conn).await?;
let tags: Vec<Tag> = vec![
Tag {
tag_name: "symbol".to_string(),
tag_value: symbol,
description: None,
},
tag_name: "name".to_string(),
tag_value: name,
];
self.update_commodity_tags(&c, &tags).await?;
Ok(c)
pub async fn update_commodity_tags(
c: &Commodity,
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/commodities/tags.sql",
&c.id,
&tag_ids,
&names,
&values,
)
.fetch_one(&mut *conn)
.await
.map_err(|err| {
ServerError::DB(DBError::Sqlx(err))
Ok(())
pub async fn set_commodity_tag(&self, c: &Commodity, t: &Tag) -> Result<(), ServerError> {
"sql/set/commodities/tag.sql",
&c.fraction,
&t.id,
&t.tag_name,
&t.tag_value,
t.description
pub async fn get_commodity_tags(&self, c: &Commodity) -> Result<Vec<Tag>, ServerError> {
let tags = sqlx::query_file_as!(Tag, "sql/select/commodities/tags.sql", &c.id)
.fetch_all(&mut *conn)
log::error!("Database error: {err:?}");
Ok(tags)
pub async fn get_commodity_tag(
tag: &String,
) -> Result<Tag, ServerError> {
let tag = sqlx::query_file_as!(Tag, "sql/select/commodities/tag.sql", &c.id, tag)
Ok(tag)
#[cfg(test)]
mod commodity_tests {
use super::*;
use crate::db::DB_POOL;
#[cfg(feature = "testlog")]
use env_logger;
use log;
use sqlx::PgPool;
use std::fs;
use supp_macro::local_db_sqlx_test;
use tokio::sync::OnceCell;
/// Context for keeping environment intact
static CONTEXT: OnceCell<()> = 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;
USER.get_or_init(|| async { User { id: Uuid::new_v4() } })
#[local_db_sqlx_test]
async fn test_commodity_creation(pool: PgPool) -> Result<(), anyhow::Error> {
USER.get()
.unwrap()
.commit()
.expect("Failed to commit user to database");
let mut conn = pool.acquire().await.unwrap();
let commodity = Commodity {
fraction: 100,
let user = USER.get().unwrap();
sqlx::query!(
"INSERT INTO commodities (id, fraction) \
VALUES ($1, $2)",
&commodity.id,
&commodity.fraction
.execute(&mut *conn)
.unwrap();
let script = fs::read("../target/commodity.wasm")?;
let comm = user.add_commodity(&script).await?;
assert_eq!(comm.fraction, 15);
async fn test_create_commodity(pool: PgPool) -> Result<(), anyhow::Error> {
user.commit()
let c = user
.create_commodity(15, "JPY".to_string(), "Japanese Yen".to_string())
.await?;
let res = sqlx::query!("SELECT fraction FROM commodities WHERE id = $1", c.id)
assert_eq!(res.fraction, 15);
let tag = user.get_commodity_tag(&c, &"symbol".to_string()).await?;
assert_eq!(tag.tag_value, "JPY");
let tag = user.get_commodity_tag(&c, &"name".to_string()).await?;
assert_eq!(tag.tag_value, "Japanese Yen");
async fn test_commodity_tag(pool: PgPool) {
let mut conn = user.get_connection().await?;
user.set_commodity_tag(
&commodity,
&Tag::builder()
.tag_name("test")
.tag_value("testval")
.build()?,
let res = sqlx::query_file!("testdata/query_tag.sql", &commodity.fraction)
assert_eq!(res.tag_name_result, "test".to_string());
assert_eq!(res.tag_value_result, "testval".to_string());
.tag_value("testval2")
assert_eq!(res.tag_value_result, "testval2".to_string());
async fn test_get_commodity_tags(pool: PgPool) {
let tags = user.get_commodity_tags(&commodity).await?;
assert_eq!(tags.len(), 1);
assert_eq!(tags.first().unwrap().tag_name, "test".to_string());
.tag_name("test2")
assert_eq!(tags.len(), 2);
assert_eq!(tags.last().unwrap().tag_name, "test2".to_string());
.tag_name("newname")
.tag_value("the new full name of the Yen")
let mut tagdb_lock = tagdb
.lock()
.map_err(|e| anyhow::anyhow!("Mutex is poisoned: {}", e))?;
let comm = apply_commodity_hook(commodity, tagdb, &script).await?;
// assert_eq!(
// comm.fullname,
// Some("the new full name of the Yen".to_string())
// );
let mut newtags = user.get_commodity_tags(&comm).await?;
let name = newtags[0].tag_name.clone();
newtags[0].tag_value = "thenewval".to_string();
user.update_commodity_tags(&comm, &newtags).await?;
let updated_tags = user.get_commodity_tags(&comm).await?;
if let Some(tag) = updated_tags.iter().find(|t| t.tag_name == *name) {
assert_eq!(
tag.tag_value, "thenewval",
"The tag value was not updated correctly"
);
panic!("Tag with name '{name}' not found in updated tags");