Lines
94.74 %
Functions
100 %
Branches
//! Unit tests for the commodity-domain natives. The `format_*` / `quote_string`
//! helpers are test-only fixtures retained from the pre-WasmGC string envelope.
use super::*;
use finance::commodity::Commodity;
use std::collections::HashMap;
use uuid::Uuid;
/// Renders the TaggedEntities result as the previous self-capturing string
/// envelope; retained for the format fixtures below.
fn format_tagged_commodities(
entities: &[(
FinanceEntity,
std::collections::HashMap<String, FinanceEntity>,
)],
) -> String {
let mut out = String::from("(:commodities (");
for (idx, (entity, tags)) in entities.iter().enumerate() {
if idx > 0 {
out.push(' ');
}
let id = match entity {
FinanceEntity::Commodity(c) => c.id,
other => {
out.push_str(&format!("(:error \"unexpected entity {other:?}\")"));
continue;
};
out.push_str(&format!("(:id \"{id}\""));
if let Some(symbol) = tag_value(tags, "symbol") {
out.push_str(&format!(" :symbol {}", quote_string(symbol)));
if let Some(name) = tag_value(tags, "name") {
out.push_str(&format!(" :name {}", quote_string(name)));
out.push(')');
out.push_str("))");
out
fn quote_string(s: &str) -> String {
let mut q = String::with_capacity(s.len() + 2);
q.push('"');
for ch in s.chars() {
match ch {
'"' => q.push_str("\\\""),
'\\' => q.push_str("\\\\"),
other => q.push(other),
q
fn commodity_entity(id: Uuid) -> FinanceEntity {
FinanceEntity::Commodity(Commodity { id })
#[test]
fn format_empty_list() {
assert_eq!(format_tagged_commodities(&[]), "(:commodities ())");
fn format_single_commodity_with_symbol_and_name() {
let id = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
let mut tags = HashMap::new();
tags.insert(
"symbol".to_string(),
FinanceEntity::Tag(Tag {
id: Uuid::nil(),
tag_name: "symbol".into(),
tag_value: "USD".into(),
description: None,
}),
);
"name".to_string(),
tag_name: "name".into(),
tag_value: "US Dollar".into(),
let out = format_tagged_commodities(&[(commodity_entity(id), tags)]);
assert!(out.contains(":id \"550e8400-e29b-41d4-a716-446655440000\""));
assert!(out.contains(":symbol \"USD\""));
assert!(out.contains(":name \"US Dollar\""));
fn parse_amount_str_integer() {
assert_eq!(parse_amount_str("100").unwrap(), (100, 1));
fn parse_amount_str_fraction() {
assert_eq!(parse_amount_str("153/100").unwrap(), (153, 100));
fn parse_amount_str_negative_integer() {
assert_eq!(parse_amount_str("-42").unwrap(), (-42, 1));
fn parse_amount_str_negative_fraction() {
assert_eq!(parse_amount_str("-153/100").unwrap(), (-153, 100));
fn parse_amount_str_empty_is_error() {
assert!(parse_amount_str("").is_err());
fn parse_amount_str_garbage_is_error() {
assert!(parse_amount_str("abc").is_err());
fn parse_amount_str_zero_denom_is_error() {
let err = parse_amount_str("1/0").unwrap_err();
assert!(err.to_string().contains("denominator must be positive"));
fn parse_amount_str_negative_denom_is_error() {
let err = parse_amount_str("1/-100").unwrap_err();
fn parse_amount_str_multiple_slashes_is_error() {
assert!(parse_amount_str("1/2/3").is_err());
#[tokio::test]
async fn run_create_commodity_missing_symbol_emits_error() {
let err = run_create_commodity(Uuid::nil(), None, Some("name".into()))
.await
.unwrap_err();
assert!(err.to_string().contains(":symbol"));
async fn run_create_commodity_missing_name_emits_error() {
let err = run_create_commodity(Uuid::nil(), Some("sym".into()), None)
assert!(err.to_string().contains(":name"));
fn format_commodity_without_tags_emits_id_only() {
let id = Uuid::nil();
let out = format_tagged_commodities(&[(commodity_entity(id), HashMap::new())]);
assert_eq!(
out,
"(:commodities ((:id \"00000000-0000-0000-0000-000000000000\")))"