use super::*;
use crate::CurrencyError;
#[cfg(test)]
mod currency_tests {
use super::*;
#[test]
fn test_currency_creation_valid() {
let usd = Currency::new("USD").unwrap();
assert_eq!(usd.code(), "USD");
let eur = Currency::new("eur").unwrap(); assert_eq!(eur.code(), "EUR");
}
#[test]
fn test_currency_creation_invalid() {
assert!(Currency::new("US").is_err());
assert!(Currency::new("USDD").is_err());
assert!(Currency::new("US1").is_err());
assert!(Currency::new("US$").is_err());
assert!(Currency::new("").is_err());
}
#[test]
fn test_currency_equality() {
let usd1 = Currency::new("USD").unwrap();
let usd2 = Currency::new("usd").unwrap();
let eur = Currency::new("EUR").unwrap();
assert!(usd1.is_same_as(&usd2));
assert!(!usd1.is_same_as(&eur));
}
#[test]
fn test_currency_display() {
let usd = Currency::new("USD").unwrap();
assert_eq!(format!("{}", usd), "USD");
}
#[test]
fn test_currency_try_from() {
let usd: Currency = "USD".try_into().unwrap();
assert_eq!(usd.code(), "USD");
let invalid: std::result::Result<Currency, CurrencyError> = "INVALID".try_into();
assert!(invalid.is_err());
}
}
#[cfg(test)]
mod money_tests {
use super::*;
#[test]
fn test_money_creation_valid() {
let currency = Currency::new("USD").unwrap();
let money = Money::new(100.0, currency).unwrap();
assert_eq!(money.amount(), 100.0);
assert_eq!(money.currency().code(), "USD");
}
#[test]
fn test_money_from_code() {
let money = Money::from_code(50.0, "EUR").unwrap();
assert_eq!(money.amount(), 50.0);
assert_eq!(money.currency().code(), "EUR");
}
#[test]
fn test_money_invalid_amounts() {
assert!(Money::from_code(-100.0, "USD").is_err());
assert!(Money::from_code(f64::NAN, "USD").is_err());
assert!(Money::from_code(f64::INFINITY, "USD").is_err());
assert!(Money::from_code(1e15, "USD").is_err());
}
#[test]
fn test_money_rounding() {
let money = Money::from_code(123.456789, "USD").unwrap();
let rounded = money.round(2);
assert_eq!(rounded.amount(), 123.46);
assert_eq!(rounded.currency().code(), "USD");
}
#[test]
fn test_money_display() {
let money = Money::from_code(123.45, "USD").unwrap();
assert_eq!(format!("{}", money), "123.45 USD");
}
}
#[cfg(test)]
mod conversion_request_tests {
use super::*;
#[test]
fn test_conversion_request_creation() {
let from = Money::from_code(100.0, "USD").unwrap();
let to = Currency::new("EUR").unwrap();
let request = ConversionRequest::new(from, to);
assert_eq!(request.from.amount(), 100.0);
assert_eq!(request.from.currency().code(), "USD");
assert_eq!(request.to.code(), "EUR");
}
#[test]
fn test_conversion_request_from_components() {
let request = ConversionRequest::from_components(50.0, "GBP", "JPY").unwrap();
assert_eq!(request.from.amount(), 50.0);
assert_eq!(request.from.currency().code(), "GBP");
assert_eq!(request.to.code(), "JPY");
}
#[test]
fn test_same_currency_detection() {
let same_request = ConversionRequest::from_components(100.0, "USD", "USD").unwrap();
assert!(same_request.is_same_currency());
let different_request = ConversionRequest::from_components(100.0, "USD", "EUR").unwrap();
assert!(!different_request.is_same_currency());
}
#[test]
fn test_conversion_request_invalid_inputs() {
assert!(ConversionRequest::from_components(100.0, "INVALID", "EUR").is_err());
assert!(ConversionRequest::from_components(100.0, "USD", "INVALID").is_err());
assert!(ConversionRequest::from_components(-100.0, "USD", "EUR").is_err());
}
}
#[cfg(test)]
mod conversion_result_tests {
use super::*;
#[test]
fn test_conversion_result_creation() {
let request = ConversionRequest::from_components(100.0, "USD", "EUR").unwrap();
let result_money = Money::from_code(85.0, "EUR").unwrap();
let result = ConversionResult::new(request, result_money, 0.85, ConversionType::Direct);
assert_eq!(result.exchange_rate, 0.85);
assert_eq!(result.result.amount(), 85.0);
assert_eq!(result.conversion_type, ConversionType::Direct);
assert!(result.timestamp > 0);
}
#[test]
fn test_conversion_type_variants() {
let request = ConversionRequest::from_components(100.0, "USD", "EUR").unwrap();
let result_money = Money::from_code(85.0, "EUR").unwrap();
let same_result = ConversionResult::new(
request.clone(),
result_money.clone(),
1.0,
ConversionType::SameCurrency,
);
assert_eq!(same_result.conversion_type, ConversionType::SameCurrency);
let cross_result = ConversionResult::new(
request.clone(),
result_money.clone(),
0.85,
ConversionType::Cross {
via_currency: "USD".to_string(),
},
);
if let ConversionType::Cross { via_currency } = cross_result.conversion_type {
assert_eq!(via_currency, "USD");
} else {
panic!("Expected Cross conversion type");
}
}
#[test]
fn test_conversion_result_summary() {
let request = ConversionRequest::from_components(100.0, "USD", "EUR").unwrap();
let result_money = Money::from_code(85.0, "EUR").unwrap();
let result = ConversionResult::new(request, result_money, 0.85, ConversionType::Direct);
let summary = result.summary();
assert!(summary.contains("100.00 USD"));
assert!(summary.contains("85.00 EUR"));
assert!(summary.contains("0.850000"));
}
}
#[cfg(test)]
mod property_tests {
use super::*;
use proptest::prelude::*;
proptest! {
#[test]
fn test_currency_code_roundtrip(
code in "[A-Z]{3}"
) {
let currency = Currency::new(&code).unwrap();
assert_eq!(currency.code(), code);
}
#[test]
fn test_money_rounding_properties(
amount in 0.01f64..1_000_000.0,
places in 0u32..6u32
) {
let money = Money::from_code(amount, "USD").unwrap();
let rounded = money.round(places);
let expected_multiplier = 10_f64.powi(places as i32);
let expected = (amount * expected_multiplier).round() / expected_multiplier;
assert!((rounded.amount() - expected).abs() < f64::EPSILON);
}
#[test]
fn test_conversion_request_validation(
amount in 0.01f64..1_000_000.0,
from_code in "[A-Z]{3}",
to_code in "[A-Z]{3}"
) {
let request = ConversionRequest::from_components(amount, &from_code, &to_code);
assert!(request.is_ok());
let req = request.unwrap();
assert_eq!(req.from.amount(), amount);
assert_eq!(req.from.currency().code(), from_code);
assert_eq!(req.to.code(), to_code);
}
}
}