342 lines
11 KiB
Rust
342 lines
11 KiB
Rust
use rstest::*;
|
|
use steel_decimal::*;
|
|
|
|
// Basic Arithmetic Tests
|
|
#[rstest]
|
|
#[case("1.5", "2.3", "3.8")]
|
|
#[case("10", "5", "15")]
|
|
#[case("-5.5", "3.2", "-2.3")]
|
|
#[case("0", "42", "42")]
|
|
fn test_decimal_add(#[case] a: &str, #[case] b: &str, #[case] expected: &str) {
|
|
let result = decimal_add(a.to_string(), b.to_string()).unwrap();
|
|
assert_eq!(result, expected);
|
|
}
|
|
|
|
#[rstest]
|
|
#[case("10", "3", "7")]
|
|
#[case("5.5", "2.2", "3.3")]
|
|
#[case("0", "5", "-5")]
|
|
#[case("-3", "-2", "-1")]
|
|
fn test_decimal_sub(#[case] a: &str, #[case] b: &str, #[case] expected: &str) {
|
|
let result = decimal_sub(a.to_string(), b.to_string()).unwrap();
|
|
assert_eq!(result, expected);
|
|
}
|
|
|
|
#[rstest]
|
|
#[case("2", "3", "6")]
|
|
#[case("2.5", "4", "10.0")] // rust_decimal preserves precision
|
|
#[case("-2", "3", "-6")]
|
|
#[case("0", "100", "0")]
|
|
fn test_decimal_mul(#[case] a: &str, #[case] b: &str, #[case] expected: &str) {
|
|
let result = decimal_mul(a.to_string(), b.to_string()).unwrap();
|
|
assert_eq!(result, expected);
|
|
}
|
|
|
|
#[rstest]
|
|
#[case("10", "2", "5")]
|
|
#[case("15", "3", "5")]
|
|
#[case("7.5", "2.5", "3")]
|
|
fn test_decimal_div(#[case] a: &str, #[case] b: &str, #[case] expected: &str) {
|
|
let result = decimal_div(a.to_string(), b.to_string()).unwrap();
|
|
assert_eq!(result, expected);
|
|
}
|
|
|
|
#[rstest]
|
|
fn test_decimal_div_by_zero() {
|
|
let result = decimal_div("10".to_string(), "0".to_string());
|
|
assert!(result.is_err());
|
|
assert!(result.unwrap_err().contains("Division by zero"));
|
|
}
|
|
|
|
// Advanced Math Tests
|
|
#[rstest]
|
|
#[case("2", "3", "8")]
|
|
#[case("5", "2", "25")]
|
|
#[case("10", "0", "1")]
|
|
fn test_decimal_pow(#[case] base: &str, #[case] exp: &str, #[case] expected: &str) {
|
|
let result = decimal_pow(base.to_string(), exp.to_string()).unwrap();
|
|
assert_eq!(result, expected);
|
|
}
|
|
|
|
#[rstest]
|
|
#[case("16")]
|
|
#[case("25")]
|
|
#[case("9")]
|
|
fn test_decimal_sqrt(#[case] input: &str) {
|
|
let result = decimal_sqrt(input.to_string()).unwrap();
|
|
// rust_decimal sqrt returns high precision - just verify it starts with the right digit
|
|
match input {
|
|
"16" => assert!(result.starts_with("4")),
|
|
"25" => assert!(result.starts_with("5")),
|
|
"9" => assert!(result.starts_with("3")),
|
|
_ => panic!("Unexpected input"),
|
|
}
|
|
}
|
|
|
|
#[rstest]
|
|
fn test_decimal_sqrt_negative() {
|
|
let result = decimal_sqrt("-4".to_string());
|
|
assert!(result.is_err());
|
|
assert!(result.unwrap_err().contains("Square root failed"));
|
|
}
|
|
|
|
// Comparison Tests
|
|
#[rstest]
|
|
#[case("5", "3", true)]
|
|
#[case("3", "5", false)]
|
|
#[case("5", "5", false)]
|
|
fn test_decimal_gt(#[case] a: &str, #[case] b: &str, #[case] expected: bool) {
|
|
let result = decimal_gt(a.to_string(), b.to_string()).unwrap();
|
|
assert_eq!(result, expected);
|
|
}
|
|
|
|
#[rstest]
|
|
#[case("3", "5", true)]
|
|
#[case("5", "3", false)]
|
|
#[case("5", "5", false)]
|
|
fn test_decimal_lt(#[case] a: &str, #[case] b: &str, #[case] expected: bool) {
|
|
let result = decimal_lt(a.to_string(), b.to_string()).unwrap();
|
|
assert_eq!(result, expected);
|
|
}
|
|
|
|
#[rstest]
|
|
#[case("5", "5", true)]
|
|
#[case("5", "3", false)]
|
|
#[case("3.14", "3.14", true)]
|
|
fn test_decimal_eq(#[case] a: &str, #[case] b: &str, #[case] expected: bool) {
|
|
let result = decimal_eq(a.to_string(), b.to_string()).unwrap();
|
|
assert_eq!(result, expected);
|
|
}
|
|
|
|
#[rstest]
|
|
#[case("5", "3", true)]
|
|
#[case("3", "5", false)]
|
|
#[case("5", "5", true)]
|
|
fn test_decimal_gte(#[case] a: &str, #[case] b: &str, #[case] expected: bool) {
|
|
let result = decimal_gte(a.to_string(), b.to_string()).unwrap();
|
|
assert_eq!(result, expected);
|
|
}
|
|
|
|
#[rstest]
|
|
#[case("3", "5", true)]
|
|
#[case("5", "3", false)]
|
|
#[case("5", "5", true)]
|
|
fn test_decimal_lte(#[case] a: &str, #[case] b: &str, #[case] expected: bool) {
|
|
let result = decimal_lte(a.to_string(), b.to_string()).unwrap();
|
|
assert_eq!(result, expected);
|
|
}
|
|
|
|
// Utility Tests
|
|
#[rstest]
|
|
#[case("-5", "5")]
|
|
#[case("3.14", "3.14")]
|
|
#[case("-0", "0")]
|
|
fn test_decimal_abs(#[case] input: &str, #[case] expected: &str) {
|
|
let result = decimal_abs(input.to_string()).unwrap();
|
|
assert_eq!(result, expected);
|
|
}
|
|
|
|
#[rstest]
|
|
#[case("3", "5", "3")]
|
|
#[case("10", "7", "7")]
|
|
#[case("-5", "-2", "-5")]
|
|
fn test_decimal_min(#[case] a: &str, #[case] b: &str, #[case] expected: &str) {
|
|
let result = decimal_min(a.to_string(), b.to_string()).unwrap();
|
|
assert_eq!(result, expected);
|
|
}
|
|
|
|
#[rstest]
|
|
#[case("3", "5", "5")]
|
|
#[case("10", "7", "10")]
|
|
#[case("-5", "-2", "-2")]
|
|
fn test_decimal_max(#[case] a: &str, #[case] b: &str, #[case] expected: &str) {
|
|
let result = decimal_max(a.to_string(), b.to_string()).unwrap();
|
|
assert_eq!(result, expected);
|
|
}
|
|
|
|
#[rstest]
|
|
#[case("3.14159", 2, "3.14")]
|
|
#[case("2.71828", 3, "2.718")]
|
|
#[case("10.999", 0, "11")]
|
|
fn test_decimal_round(#[case] input: &str, #[case] places: i32, #[case] expected: &str) {
|
|
let result = decimal_round(input.to_string(), places).unwrap();
|
|
assert_eq!(result, expected);
|
|
}
|
|
|
|
// Constants Tests
|
|
#[rstest]
|
|
fn test_decimal_constants() {
|
|
assert_eq!(decimal_zero(), "0");
|
|
assert_eq!(decimal_one(), "1");
|
|
assert!(decimal_pi().starts_with("3.14159"));
|
|
assert!(decimal_e().starts_with("2.71828"));
|
|
}
|
|
|
|
// Financial Functions Tests
|
|
#[rstest]
|
|
#[case("100", "15", "15")]
|
|
#[case("1000", "5.5", "55.0")] // rust_decimal preserves precision from 5.5
|
|
#[case("250", "20", "50")]
|
|
fn test_decimal_percentage(#[case] amount: &str, #[case] percentage: &str, #[case] expected: &str) {
|
|
let result = decimal_percentage(amount.to_string(), percentage.to_string()).unwrap();
|
|
assert_eq!(result, expected);
|
|
}
|
|
|
|
#[rstest]
|
|
#[case("1000", "0.05", "1", "1050.00")] // rust_decimal preserves precision from 0.05
|
|
#[case("1000", "0.1", "2", "1210.00")] // rust_decimal preserves precision from 0.1
|
|
fn test_decimal_compound(#[case] principal: &str, #[case] rate: &str, #[case] time: &str, #[case] expected: &str) {
|
|
let result = decimal_compound(principal.to_string(), rate.to_string(), time.to_string()).unwrap();
|
|
assert_eq!(result, expected);
|
|
}
|
|
|
|
// Type Conversion Tests
|
|
#[rstest]
|
|
#[case("123.456", "123.456")]
|
|
#[case("42", "42")]
|
|
#[case("0.001", "0.001")]
|
|
fn test_to_decimal(#[case] input: &str, #[case] expected: &str) {
|
|
let result = to_decimal(input.to_string()).unwrap();
|
|
assert_eq!(result, expected);
|
|
}
|
|
|
|
#[rstest]
|
|
fn test_to_decimal_invalid() {
|
|
let result = to_decimal("not_a_number".to_string());
|
|
assert!(result.is_err());
|
|
assert!(result.unwrap_err().contains("Invalid decimal"));
|
|
}
|
|
|
|
// Error Handling Tests
|
|
#[rstest]
|
|
#[case("invalid", "2")]
|
|
#[case("1", "invalid")]
|
|
#[case("abc", "def")]
|
|
fn test_decimal_add_invalid_input(#[case] a: &str, #[case] b: &str) {
|
|
let result = decimal_add(a.to_string(), b.to_string());
|
|
assert!(result.is_err());
|
|
}
|
|
|
|
#[rstest]
|
|
#[case("invalid")]
|
|
#[case("not_a_number")]
|
|
#[case("abc")]
|
|
fn test_decimal_sqrt_invalid_input(#[case] input: &str) {
|
|
let result = decimal_sqrt(input.to_string());
|
|
assert!(result.is_err());
|
|
}
|
|
|
|
// Edge Cases
|
|
#[rstest]
|
|
#[case("0.000000001", "0.000000001", "0.000000002")]
|
|
#[case("999999999999999", "1", "1000000000000000")]
|
|
fn test_decimal_precision_edge_cases(#[case] a: &str, #[case] b: &str, #[case] expected: &str) {
|
|
let result = decimal_add(a.to_string(), b.to_string()).unwrap();
|
|
assert_eq!(result, expected);
|
|
}
|
|
|
|
// Test scientific notation
|
|
#[rstest]
|
|
#[case("1e2", "2e1", "120")]
|
|
#[case("1.5e2", "2.3e1", "173.0")]
|
|
fn test_scientific_notation(#[case] a: &str, #[case] b: &str, #[case] expected: &str) {
|
|
let result = decimal_add(a.to_string(), b.to_string()).unwrap();
|
|
assert_eq!(result, expected);
|
|
}
|
|
|
|
// Test precision behavior
|
|
#[rstest]
|
|
#[case("5", "0", "5")] // Integer + integer = integer
|
|
#[case("5.0", "0", "5.0")] // Decimal + integer = decimal
|
|
#[case("5.00", "0.00", "5.00")] // Preserves highest precision
|
|
#[case("5.1", "0.23", "5.33")] // Normal decimal arithmetic
|
|
fn test_precision_preservation(#[case] a: &str, #[case] b: &str, #[case] expected: &str) {
|
|
let result = decimal_add(a.to_string(), b.to_string()).unwrap();
|
|
assert_eq!(result, expected);
|
|
}
|
|
|
|
// Test explicit precision using global precision setting
|
|
#[rstest]
|
|
#[case("5.123", "2.456", 0, "8")] // 0 decimal places
|
|
#[case("5.123", "2.456", 2, "7.58")] // 2 decimal places
|
|
#[case("5.12312", "2.45622", 4, "7.5793")] // 4 decimal places
|
|
fn test_explicit_precision(#[case] a: &str, #[case] b: &str, #[case] precision: u32, #[case] expected: &str) {
|
|
// Set precision globally
|
|
set_precision(precision);
|
|
|
|
// Test with precision set
|
|
let result = decimal_add(a.to_string(), b.to_string()).unwrap();
|
|
assert_eq!(result, expected);
|
|
|
|
// Clean up - clear precision for other tests
|
|
clear_precision();
|
|
}
|
|
|
|
// Test precision functions
|
|
#[test]
|
|
fn test_precision_functions() {
|
|
// Test setting precision
|
|
assert_eq!(set_precision(2), "Precision set to 2 decimal places");
|
|
assert_eq!(get_precision(), "2");
|
|
|
|
// Test with precision set
|
|
let result = decimal_add("1.567".to_string(), "2.891".to_string()).unwrap();
|
|
assert_eq!(result, "4.46");
|
|
|
|
// Test clearing precision
|
|
assert_eq!(clear_precision(), "Precision cleared - using full precision");
|
|
assert_eq!(get_precision(), "full");
|
|
|
|
// Test with full precision
|
|
let result = decimal_add("1.567".to_string(), "2.891".to_string()).unwrap();
|
|
assert_eq!(result, "4.458");
|
|
}
|
|
|
|
// Test decimal_format function
|
|
#[rstest]
|
|
#[case("5.123456", 2, "5.12")]
|
|
#[case("5.123456", 4, "5.1235")]
|
|
#[case("5.999", 0, "6")]
|
|
fn test_decimal_format(#[case] value: &str, #[case] precision: u32, #[case] expected: &str) {
|
|
let result = decimal_format(value.to_string(), precision).unwrap();
|
|
assert_eq!(result, expected);
|
|
}
|
|
|
|
// Test precision doesn't affect comparison functions
|
|
#[test]
|
|
fn test_precision_does_not_affect_comparisons() {
|
|
set_precision(2);
|
|
|
|
// Comparisons should use full precision internally
|
|
assert_eq!(decimal_gt("1.567".to_string(), "1.566".to_string()).unwrap(), true);
|
|
assert_eq!(decimal_eq("1.567".to_string(), "1.567".to_string()).unwrap(), true);
|
|
assert_eq!(decimal_eq("1.567".to_string(), "1.57".to_string()).unwrap(), false);
|
|
|
|
clear_precision();
|
|
}
|
|
|
|
// Test precision edge cases
|
|
#[test]
|
|
fn test_precision_edge_cases() {
|
|
// Test max precision
|
|
assert_eq!(set_precision(28), "Precision set to 28 decimal places");
|
|
|
|
// Test beyond max precision
|
|
assert_eq!(set_precision(29), "Error: Maximum precision is 28 decimal places");
|
|
|
|
// Clean up
|
|
clear_precision();
|
|
}
|
|
|
|
// Test scientific notation edge cases
|
|
#[rstest]
|
|
#[case("1e0", "1")] // Simple case
|
|
#[case("1.0e0", "1.0")] // Preserves decimal
|
|
#[case("1e-2", "0.01")] // Negative exponent
|
|
#[case("1.5e-3", "0.0015")] // Decimal + negative exponent
|
|
#[case("2.5e2", "250.0")] // Decimal + positive exponent
|
|
fn test_scientific_edge_cases(#[case] input: &str, #[case] expected: &str) {
|
|
let result = to_decimal(input.to_string()).unwrap();
|
|
assert_eq!(result, expected);
|
|
}
|