Lines
100 %
Functions
17.96 %
Branches
use finance::{commodity::Commodity, tag::Tag};
use num_rational::Rational64;
use sqlx::types::Uuid;
use std::{collections::HashMap, fmt::Debug};
use supp_macro::command;
use super::{CmdError, CmdResult};
use crate::{command::FinanceEntity, config::ConfigError, user::User};
command! {
GetCommodity {
#[required]
user_id: Uuid,
commodity_id: Uuid,
} => {
let user = User { id: user_id };
let mut conn = user.get_connection().await.map_err(|err| {
log::error!("{}", t!("Database error: %{err}", err = err : {:?}));
ConfigError::DB
})?;
let comm = sqlx::query_file_as!(Commodity, "sql/select/commodities/by_id.sql", &commodity_id)
.fetch_one(&mut *conn)
.await?;
// For each commodity, get its tags
let mut tagged_entities = Vec::new();
let tags: HashMap<String, FinanceEntity> =
sqlx::query_file!("sql/select/tags/by_commodity.sql", &commodity_id)
.fetch_all(&mut *conn)
.await?
.into_iter()
.map(|row| {
(
row.tag_name.clone(),
FinanceEntity::Tag(Tag {
id: row.id,
tag_name: row.tag_name,
tag_value: row.tag_value,
description: row.description,
}),
)
})
.collect();
tagged_entities.push((FinanceEntity::Commodity(comm), tags));
Ok(Some(CmdResult::TaggedEntities(tagged_entities)))
}
CreateCommodity {
fraction: Rational64,
symbol: String,
name: String,
Ok(Some(
user.create_commodity(fraction.to_integer(), symbol, name)
.id
.to_string()
.into(),
))
ListCommodities {
// Get all commodities
let commodities: Vec<Commodity> = sqlx::query_file!("sql/select/commodities/all.sql")
.map(|row| Commodity {
fraction: row.fraction,
for commodity in commodities {
sqlx::query_file!("sql/select/tags/by_commodity.sql", &commodity.id)
tagged_entities.push((FinanceEntity::Commodity(commodity), tags));
#[cfg(test)]
mod command_tests {
use super::*;
use crate::db::DB_POOL;
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 USER: OnceCell<User> = OnceCell::const_new();
async fn setup() {
CONTEXT
.get_or_init(|| async {
#[cfg(feature = "testlog")]
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_list_commodities_empty(pool: PgPool) -> anyhow::Result<()> {
let user = USER.get().unwrap();
user.commit()
.await
.expect("Failed to commit user to database");
if let Some(CmdResult::TaggedEntities(entities)) =
ListCommodities::new().user_id(user.id).run().await?
{
assert!(
entities.is_empty(),
"Expected no commodities in empty database"
);
} else {
panic!("Expected TaggedEntities result");
async fn test_list_commodities_with_data(pool: PgPool) -> anyhow::Result<()> {
// Create a test commodity with tags
CreateCommodity::new()
.fraction(1.into())
.symbol("TST".to_string())
.name("Test Commodity".to_string())
.user_id(user.id)
.run()
// List commodities
assert_eq!(entities.len(), 1, "Expected one commodity");
let (entity, tags) = &entities[0];
if let FinanceEntity::Commodity(c) = entity {
assert_eq!(c.fraction, 1);
// Check tags
assert_eq!(tags.len(), 2); // symbol and name tags
for tag in tags.values() {
if let FinanceEntity::Tag(t) = tag {
match t.tag_name.as_str() {
"symbol" => assert_eq!(t.tag_value, "TST"),
"name" => assert_eq!(t.tag_value, "Test Commodity"),
_ => panic!("Unexpected tag: {}", t.tag_name),
panic!("Expected Commodity entity");
async fn test_get_commodity(pool: PgPool) -> anyhow::Result<()> {
// First create a commodity
let commodity_result = CreateCommodity::new()
// Get the commodity ID
let commodity_id = if let Some(CmdResult::String(id)) = commodity_result {
uuid::Uuid::parse_str(&id)?
panic!("Expected commodity ID string result");
};
// Test GetCommodity command
if let Some(CmdResult::TaggedEntities(entities)) = GetCommodity::new()
.commodity_id(commodity_id)
assert_eq!(c.id, commodity_id);
// Test with non-existent commodity ID
let result = GetCommodity::new()
.commodity_id(Uuid::new_v4())
assert!(result.is_err(), "Expected error for non-existent commodity");