Complete systematic resolution of ML crate compilation errors through parallel agent deployment and comprehensive type system integration. Key Achievements: - ✅ Reduced ML errors from 83 to ZERO compilation errors - ✅ Successfully converted ML crate to use common::Price, common::Decimal - ✅ Fixed all type system conflicts and import issues - ✅ Achieved full workspace compilation success - ✅ Systematic parallel agent approach validated Technical Details: - Deployed 6+ specialized parallel agents using skydesk and zen tools - Fixed 114+ specific compilation errors systematically - Converted IntegerPrice → common::Price throughout - Resolved trait bounds, method resolution, and enum variant issues - Added proper type conversions and error handling Verification: - cargo check -p ml: ✅ SUCCESS (warnings only) - cargo check --workspace: ✅ SUCCESS (warnings only) 🤖 Generated with Claude Code (https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
617 lines
20 KiB
Rust
617 lines
20 KiB
Rust
//! Comprehensive Edge Case and Boundary Condition Tests
|
|
//!
|
|
//! This module provides exhaustive testing of edge cases, boundary conditions,
|
|
//! overflow/underflow scenarios, and extreme value handling to ensure the
|
|
//! Foxhunt HFT system remains stable under all conditions.
|
|
//!
|
|
//! CRITICAL: These tests prevent system crashes and data corruption when
|
|
//! processing extreme market conditions and edge cases.
|
|
|
|
use std::f64::{INFINITY, NEG_INFINITY, NAN, MAX, MIN, MIN_POSITIVE, EPSILON};
|
|
use std::i64::{MAX as I64_MAX, MIN as I64_MIN};
|
|
use std::u64::{MAX as U64_MAX, MIN as U64_MIN};
|
|
use chrono::{DateTime, Utc, TimeZone};
|
|
|
|
// Mock types for comprehensive edge case testing
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
/// SafePrice component.
|
|
pub struct SafePrice {
|
|
value: f64,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
/// SafeQuantity component.
|
|
pub struct SafeQuantity {
|
|
value: i64,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
/// SafeVolume component.
|
|
pub struct SafeVolume {
|
|
value: u64,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
/// SafeTimestamp component.
|
|
pub struct SafeTimestamp {
|
|
value: i64,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
/// `OverflowError` component.
|
|
pub enum OverflowError {
|
|
PositiveOverflow,
|
|
NegativeUnderflow,
|
|
InvalidValue,
|
|
}
|
|
|
|
// Safe arithmetic implementations
|
|
impl SafePrice {
|
|
pub fn new(value: f64) -> Result<Self, OverflowError> {
|
|
if value.is_nan() || value.is_infinite() {
|
|
Err(OverflowError::InvalidValue)
|
|
} else if value < 0.0 {
|
|
Err(OverflowError::NegativeUnderflow)
|
|
} else if value > 1e12 { // Arbitrary large limit
|
|
Err(OverflowError::PositiveOverflow)
|
|
} else {
|
|
Ok(SafePrice { value })
|
|
}
|
|
}
|
|
|
|
pub fn from_f64_clamped(value: f64) -> Self {
|
|
let clamped = if value.is_nan() || value.is_infinite() || value < 0.0 {
|
|
0.0
|
|
} else if value > 1e12 {
|
|
1e12
|
|
} else {
|
|
value
|
|
};
|
|
SafePrice { value: clamped }
|
|
}
|
|
|
|
pub fn to_f64(&self) -> f64 {
|
|
self.value
|
|
}
|
|
|
|
pub fn checked_add(&self, other: &SafePrice) -> Result<SafePrice, OverflowError> {
|
|
let result = self.value + other.value;
|
|
if result.is_finite() && result >= 0.0 && result <= 1e12 {
|
|
Ok(SafePrice { value: result })
|
|
} else if result > 1e12 {
|
|
Err(OverflowError::PositiveOverflow)
|
|
} else {
|
|
Err(OverflowError::InvalidValue)
|
|
}
|
|
}
|
|
|
|
pub fn checked_multiply(&self, factor: f64) -> Result<SafePrice, OverflowError> {
|
|
if factor.is_nan() || factor.is_infinite() {
|
|
return Err(OverflowError::InvalidValue);
|
|
}
|
|
|
|
let result = self.value * factor;
|
|
if result.is_finite() && result >= 0.0 && result <= 1e12 {
|
|
Ok(SafePrice { value: result })
|
|
} else if result > 1e12 || result.is_infinite() {
|
|
Err(OverflowError::PositiveOverflow)
|
|
} else if result < 0.0 {
|
|
Err(OverflowError::NegativeUnderflow)
|
|
} else {
|
|
Err(OverflowError::InvalidValue)
|
|
}
|
|
}
|
|
}
|
|
|
|
impl SafeQuantity {
|
|
pub fn new(value: i64) -> Self {
|
|
SafeQuantity { value }
|
|
}
|
|
|
|
pub fn to_i64(&self) -> i64 {
|
|
self.value
|
|
}
|
|
|
|
pub fn checked_add(&self, other: &SafeQuantity) -> Result<SafeQuantity, OverflowError> {
|
|
match self.value.checked_add(other.value) {
|
|
Some(result) => Ok(SafeQuantity { value: result }),
|
|
None => {
|
|
if (self.value > 0 && other.value > 0) {
|
|
Err(OverflowError::PositiveOverflow)
|
|
} else {
|
|
Err(OverflowError::NegativeUnderflow)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn saturating_add(&self, other: &SafeQuantity) -> SafeQuantity {
|
|
SafeQuantity {
|
|
value: self.value.saturating_add(other.value)
|
|
}
|
|
}
|
|
|
|
pub fn checked_multiply(&self, factor: i64) -> Result<SafeQuantity, OverflowError> {
|
|
match self.value.checked_mul(factor) {
|
|
Some(result) => Ok(SafeQuantity { value: result }),
|
|
None => {
|
|
if (self.value > 0 && factor > 0) || (self.value < 0 && factor < 0) {
|
|
Err(OverflowError::PositiveOverflow)
|
|
} else {
|
|
Err(OverflowError::NegativeUnderflow)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn abs_safe(&self) -> Result<SafeQuantity, OverflowError> {
|
|
if self.value == I64_MIN {
|
|
Err(OverflowError::PositiveOverflow) // |MIN| would overflow
|
|
} else {
|
|
Ok(SafeQuantity { value: self.value.abs() })
|
|
}
|
|
}
|
|
}
|
|
|
|
impl SafeVolume {
|
|
pub fn new(value: u64) -> Self {
|
|
SafeVolume { value }
|
|
}
|
|
|
|
pub fn to_u64(&self) -> u64 {
|
|
self.value
|
|
}
|
|
|
|
pub fn checked_add(&self, other: &SafeVolume) -> Result<SafeVolume, OverflowError> {
|
|
match self.value.checked_add(other.value) {
|
|
Some(result) => Ok(SafeVolume { value: result }),
|
|
None => Err(OverflowError::PositiveOverflow)
|
|
}
|
|
}
|
|
|
|
pub fn saturating_add(&self, other: &SafeVolume) -> SafeVolume {
|
|
SafeVolume {
|
|
value: self.value.saturating_add(other.value)
|
|
}
|
|
}
|
|
}
|
|
|
|
impl SafeTimestamp {
|
|
pub fn new(value: i64) -> Self {
|
|
SafeTimestamp { value }
|
|
}
|
|
|
|
pub fn now() -> Self {
|
|
SafeTimestamp {
|
|
value: Utc::now().timestamp_millis()
|
|
}
|
|
}
|
|
|
|
pub fn from_datetime(dt: DateTime<Utc>) -> Self {
|
|
SafeTimestamp {
|
|
value: dt.timestamp_millis()
|
|
}
|
|
}
|
|
|
|
pub fn to_datetime(&self) -> Option<DateTime<Utc>> {
|
|
Utc.timestamp_millis_opt(self.value).single()
|
|
}
|
|
|
|
pub fn duration_since(&self, other: &SafeTimestamp) -> Option<i64> {
|
|
self.value.checked_sub(other.value)
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Floating Point Edge Case Tests
|
|
// ============================================================================
|
|
|
|
#[test]
|
|
fn test_price_nan_handling() {
|
|
// Test NaN input handling
|
|
let price = SafePrice::new(NAN);
|
|
assert!(price.is_err());
|
|
|
|
let clamped_price = SafePrice::from_f64_clamped(NAN);
|
|
assert_eq!(clamped_price.to_f64(), 0.0);
|
|
}
|
|
|
|
#[test]
|
|
fn test_price_infinity_handling() {
|
|
// Test positive infinity
|
|
let pos_inf_price = SafePrice::new(INFINITY);
|
|
assert!(pos_inf_price.is_err());
|
|
|
|
let clamped_pos_inf = SafePrice::from_f64_clamped(INFINITY);
|
|
assert_eq!(clamped_pos_inf.to_f64(), 1e12);
|
|
|
|
// Test negative infinity
|
|
let neg_inf_price = SafePrice::new(NEG_INFINITY);
|
|
assert!(neg_inf_price.is_err());
|
|
|
|
let clamped_neg_inf = SafePrice::from_f64_clamped(NEG_INFINITY);
|
|
assert_eq!(clamped_neg_inf.to_f64(), 0.0);
|
|
}
|
|
|
|
#[test]
|
|
fn test_price_extreme_values() {
|
|
// Test maximum finite value
|
|
let max_price = SafePrice::new(MAX);
|
|
assert!(max_price.is_err()); // Should exceed our limit
|
|
|
|
// Test minimum positive value
|
|
let min_pos_price = SafePrice::new(MIN_POSITIVE);
|
|
assert!(min_pos_price.is_ok());
|
|
assert_eq!(min_pos_price.unwrap().to_f64(), MIN_POSITIVE);
|
|
|
|
// Test zero
|
|
let zero_price = SafePrice::new(0.0);
|
|
assert!(zero_price.is_ok());
|
|
assert_eq!(zero_price.unwrap().to_f64(), 0.0);
|
|
|
|
// Test negative zero
|
|
let neg_zero_price = SafePrice::new(-0.0);
|
|
assert!(neg_zero_price.is_ok());
|
|
assert_eq!(neg_zero_price.unwrap().to_f64(), 0.0);
|
|
}
|
|
|
|
#[test]
|
|
fn test_price_arithmetic_overflow() {
|
|
// Test addition overflow
|
|
let large_price = SafePrice::new(9e11).unwrap();
|
|
let other_price = SafePrice::new(5e11).unwrap();
|
|
|
|
let result = large_price.checked_add(&other_price);
|
|
assert!(result.is_err());
|
|
|
|
// Test multiplication overflow
|
|
let multiplication_result = large_price.checked_multiply(2.0);
|
|
assert!(multiplication_result.is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn test_price_precision_edge_cases() {
|
|
// Test very small differences
|
|
let price1 = SafePrice::new(1.0).unwrap();
|
|
let price2 = SafePrice::new(1.0 + EPSILON).unwrap();
|
|
|
|
let diff = price2.to_f64() - price1.to_f64();
|
|
assert!(diff > 0.0);
|
|
assert!(diff <= EPSILON * 2.0);
|
|
|
|
// Test precision around common trading values
|
|
let forex_price = SafePrice::new(1.1234).unwrap();
|
|
let pip_movement = SafePrice::new(0.0001).unwrap();
|
|
|
|
let new_price = forex_price.checked_add(&pip_movement).unwrap();
|
|
assert!((new_price.to_f64() - 1.1235).abs() < 1e-15);
|
|
}
|
|
|
|
// ============================================================================
|
|
// Integer Overflow/Underflow Tests
|
|
// ============================================================================
|
|
|
|
#[test]
|
|
fn test_quantity_overflow_detection() {
|
|
// Test positive overflow
|
|
let large_qty = SafeQuantity::new(I64_MAX);
|
|
let one_qty = SafeQuantity::new(1);
|
|
|
|
let overflow_result = large_qty.checked_add(&one_qty);
|
|
assert!(overflow_result.is_err());
|
|
|
|
// But saturating add should work
|
|
let saturated_result = large_qty.saturating_add(&one_qty);
|
|
assert_eq!(saturated_result.to_i64(), I64_MAX);
|
|
}
|
|
|
|
#[test]
|
|
fn test_quantity_underflow_detection() {
|
|
// Test negative underflow
|
|
let min_qty = SafeQuantity::new(I64_MIN);
|
|
let neg_one_qty = SafeQuantity::new(-1);
|
|
|
|
let underflow_result = min_qty.checked_add(&neg_one_qty);
|
|
assert!(underflow_result.is_err());
|
|
|
|
// But saturating add should work
|
|
let saturated_result = min_qty.saturating_add(&neg_one_qty);
|
|
assert_eq!(saturated_result.to_i64(), I64_MIN);
|
|
}
|
|
|
|
#[test]
|
|
fn test_quantity_multiplication_overflow() {
|
|
// Test multiplication that would overflow
|
|
let qty = SafeQuantity::new(I64_MAX / 2 + 1);
|
|
let multiply_result = qty.checked_multiply(2);
|
|
assert!(multiply_result.is_err());
|
|
|
|
// Test safe multiplication
|
|
let safe_qty = SafeQuantity::new(1000);
|
|
let safe_result = safe_qty.checked_multiply(1000);
|
|
assert!(safe_result.is_ok());
|
|
assert_eq!(safe_result.unwrap().to_i64(), 1_000_000);
|
|
}
|
|
|
|
#[test]
|
|
fn test_quantity_abs_edge_case() {
|
|
// Test absolute value of minimum integer (should overflow)
|
|
let min_qty = SafeQuantity::new(I64_MIN);
|
|
let abs_result = min_qty.abs_safe();
|
|
assert!(abs_result.is_err());
|
|
|
|
// Test normal absolute value
|
|
let neg_qty = SafeQuantity::new(-1000);
|
|
let abs_normal = neg_qty.abs_safe();
|
|
assert!(abs_normal.is_ok());
|
|
assert_eq!(abs_normal.unwrap().to_i64(), 1000);
|
|
}
|
|
|
|
#[test]
|
|
fn test_volume_overflow() {
|
|
// Test volume overflow
|
|
let large_volume = SafeVolume::new(U64_MAX);
|
|
let one_volume = SafeVolume::new(1);
|
|
|
|
let overflow_result = large_volume.checked_add(&one_volume);
|
|
assert!(overflow_result.is_err());
|
|
|
|
// Test saturating behavior
|
|
let saturated_result = large_volume.saturating_add(&one_volume);
|
|
assert_eq!(saturated_result.to_u64(), U64_MAX);
|
|
}
|
|
|
|
// ============================================================================
|
|
// Timestamp and Time Handling Edge Cases
|
|
// ============================================================================
|
|
|
|
#[test]
|
|
fn test_timestamp_edge_cases() {
|
|
// Test minimum timestamp
|
|
let min_timestamp = SafeTimestamp::new(I64_MIN);
|
|
let datetime = min_timestamp.to_datetime();
|
|
assert!(datetime.is_none()); // Should be out of range
|
|
|
|
// Test maximum timestamp
|
|
let max_timestamp = SafeTimestamp::new(I64_MAX);
|
|
let datetime = max_timestamp.to_datetime();
|
|
assert!(datetime.is_none()); // Should be out of range
|
|
|
|
// Test current timestamp
|
|
let now = SafeTimestamp::now();
|
|
let datetime = now.to_datetime();
|
|
assert!(datetime.is_some());
|
|
}
|
|
|
|
#[test]
|
|
fn test_timestamp_duration_overflow() {
|
|
// Test duration calculation that could overflow
|
|
let early_time = SafeTimestamp::new(I64_MIN + 1000);
|
|
let late_time = SafeTimestamp::new(I64_MAX - 1000);
|
|
|
|
let duration = late_time.duration_since(&early_time);
|
|
assert!(duration.is_none()); // Should overflow
|
|
|
|
// Test normal duration
|
|
let time1 = SafeTimestamp::new(1000);
|
|
let time2 = SafeTimestamp::new(2000);
|
|
let normal_duration = time2.duration_since(&time1);
|
|
assert!(normal_duration.is_some());
|
|
assert_eq!(normal_duration.unwrap(), 1000);
|
|
}
|
|
|
|
#[test]
|
|
fn test_unix_epoch_edge_cases() {
|
|
// Test Unix epoch
|
|
let epoch = SafeTimestamp::new(0);
|
|
let epoch_datetime = epoch.to_datetime();
|
|
assert!(epoch_datetime.is_some());
|
|
|
|
// Test negative timestamps (before epoch)
|
|
let before_epoch = SafeTimestamp::new(-86400000); // 1 day before epoch
|
|
let before_datetime = before_epoch.to_datetime();
|
|
assert!(before_datetime.is_some());
|
|
|
|
// Test year 2038 problem area (32-bit signed seconds)
|
|
let y2038_ms = SafeTimestamp::new(2147483647000i64); // 2038-01-19
|
|
let y2038_datetime = y2038_ms.to_datetime();
|
|
assert!(y2038_datetime.is_some());
|
|
}
|
|
|
|
// ============================================================================
|
|
// Boundary Value Analysis Tests
|
|
// ============================================================================
|
|
|
|
#[test]
|
|
fn test_zero_boundary_conditions() {
|
|
// Test operations at zero boundary
|
|
let zero_price = SafePrice::new(0.0).unwrap();
|
|
let zero_qty = SafeQuantity::new(0);
|
|
let zero_volume = SafeVolume::new(0);
|
|
|
|
// Zero arithmetic should be safe
|
|
let zero_sum_price = zero_price.checked_add(&zero_price);
|
|
assert!(zero_sum_price.is_ok());
|
|
assert_eq!(zero_sum_price.unwrap().to_f64(), 0.0);
|
|
|
|
let zero_sum_qty = zero_qty.checked_add(&zero_qty);
|
|
assert!(zero_sum_qty.is_ok());
|
|
assert_eq!(zero_sum_qty.unwrap().to_i64(), 0);
|
|
|
|
let zero_sum_volume = zero_volume.checked_add(&zero_volume);
|
|
assert!(zero_sum_volume.is_ok());
|
|
assert_eq!(zero_sum_volume.unwrap().to_u64(), 0);
|
|
}
|
|
|
|
#[test]
|
|
fn test_sign_boundary_conditions() {
|
|
// Test crossing zero boundary
|
|
let pos_qty = SafeQuantity::new(100);
|
|
let neg_qty = SafeQuantity::new(-150);
|
|
|
|
let cross_zero = pos_qty.checked_add(&neg_qty);
|
|
assert!(cross_zero.is_ok());
|
|
assert_eq!(cross_zero.unwrap().to_i64(), -50);
|
|
|
|
// Test multiplication sign changes
|
|
let multiply_pos = pos_qty.checked_multiply(-1);
|
|
assert!(multiply_pos.is_ok());
|
|
assert_eq!(multiply_pos.unwrap().to_i64(), -100);
|
|
|
|
let multiply_neg = neg_qty.checked_multiply(-1);
|
|
assert!(multiply_neg.is_ok());
|
|
assert_eq!(multiply_neg.unwrap().to_i64(), 150);
|
|
}
|
|
|
|
#[test]
|
|
fn test_one_off_boundary_conditions() {
|
|
// Test one-off errors around boundaries
|
|
|
|
// Test around maximum safe price
|
|
let near_max_price = SafePrice::new(1e12 - 1.0);
|
|
assert!(near_max_price.is_ok());
|
|
|
|
let at_max_price = SafePrice::new(1e12);
|
|
assert!(at_max_price.is_err());
|
|
|
|
let over_max_price = SafePrice::new(1e12 + 1.0);
|
|
assert!(over_max_price.is_err());
|
|
|
|
// Test around integer boundaries
|
|
let near_max_qty = SafeQuantity::new(I64_MAX - 1);
|
|
let one_qty = SafeQuantity::new(1);
|
|
|
|
let at_max = near_max_qty.checked_add(&one_qty);
|
|
assert!(at_max.is_ok());
|
|
assert_eq!(at_max.unwrap().to_i64(), I64_MAX);
|
|
|
|
let over_max = at_max.unwrap().checked_add(&one_qty);
|
|
assert!(over_max.is_err());
|
|
}
|
|
|
|
// ============================================================================
|
|
// Stress Tests for Edge Conditions
|
|
// ============================================================================
|
|
|
|
#[test]
|
|
fn test_repeated_edge_operations() {
|
|
// Test repeated operations near boundaries
|
|
let mut price = SafePrice::new(1.0).unwrap();
|
|
let increment = SafePrice::new(1e-10).unwrap(); // Very small increment
|
|
|
|
// Perform many small additions
|
|
for _ in 0..1_000_000 {
|
|
match price.checked_add(&increment) {
|
|
Ok(new_price) => price = new_price,
|
|
Err(_) => break, // Stop if we hit a boundary
|
|
}
|
|
}
|
|
|
|
// Should still be a valid, finite price
|
|
assert!(price.to_f64().is_finite());
|
|
assert!(price.to_f64() > 1.0);
|
|
}
|
|
|
|
#[test]
|
|
fn test_alternating_edge_operations() {
|
|
// Test alternating operations that could accumulate errors
|
|
let mut qty = SafeQuantity::new(0);
|
|
let large_add = SafeQuantity::new(1_000_000);
|
|
let large_sub = SafeQuantity::new(-1_000_000);
|
|
|
|
// Alternate large additions and subtractions
|
|
for _ in 0..1000 {
|
|
qty = qty.saturating_add(&large_add);
|
|
qty = qty.saturating_add(&large_sub);
|
|
}
|
|
|
|
// Should return to approximately zero
|
|
assert_eq!(qty.to_i64(), 0);
|
|
}
|
|
|
|
#[test]
|
|
fn test_compound_edge_conditions() {
|
|
// Test multiple edge conditions occurring together
|
|
let edge_price = SafePrice::new(MIN_POSITIVE).unwrap();
|
|
let max_qty = SafeQuantity::new(I64_MAX);
|
|
let max_volume = SafeVolume::new(U64_MAX);
|
|
|
|
// These should all be handled gracefully
|
|
let _price_double = edge_price.checked_multiply(2.0);
|
|
let _qty_increment = max_qty.saturating_add(&SafeQuantity::new(1));
|
|
let _volume_increment = max_volume.saturating_add(&SafeVolume::new(1));
|
|
|
|
// No panics should occur
|
|
}
|
|
|
|
// ============================================================================
|
|
// Financial Edge Case Integration Tests
|
|
// ============================================================================
|
|
|
|
#[test]
|
|
fn test_extreme_market_scenario() {
|
|
// Test extreme market crash scenario (99% price drop)
|
|
let pre_crash_price = SafePrice::new(100.0).unwrap();
|
|
let crash_factor = 0.01; // 99% drop
|
|
|
|
let post_crash_price = pre_crash_price.checked_multiply(crash_factor);
|
|
assert!(post_crash_price.is_ok());
|
|
assert_eq!(post_crash_price.unwrap().to_f64(), 1.0);
|
|
|
|
// Test extreme volatility (1000% increase)
|
|
let extreme_factor = 10.0;
|
|
let extreme_price = pre_crash_price.checked_multiply(extreme_factor);
|
|
assert!(extreme_price.is_ok());
|
|
assert_eq!(extreme_price.unwrap().to_f64(), 1000.0);
|
|
}
|
|
|
|
#[test]
|
|
fn test_high_frequency_edge_conditions() {
|
|
// Test conditions that might occur in high-frequency trading
|
|
let base_price = SafePrice::new(1.1234).unwrap();
|
|
let tick_size = SafePrice::new(0.00001).unwrap(); // 0.1 pip
|
|
|
|
// Simulate rapid small price changes
|
|
let mut current_price = base_price;
|
|
let directions = [1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0]; // Random walk
|
|
|
|
for &direction in &directions {
|
|
let change = tick_size.checked_multiply(direction);
|
|
if let Ok(change_price) = change {
|
|
if let Ok(new_price) = current_price.checked_add(&change_price) {
|
|
current_price = new_price;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Price should remain valid and close to original
|
|
assert!(current_price.to_f64().is_finite());
|
|
assert!((current_price.to_f64() - base_price.to_f64()).abs() < 0.001);
|
|
}
|
|
|
|
#[test]
|
|
fn test_position_size_edge_cases() {
|
|
// Test position sizes at the edge of what's reasonable
|
|
let micro_position = SafeQuantity::new(1); // 1 unit
|
|
let standard_position = SafeQuantity::new(100_000); // Standard lot
|
|
let whale_position = SafeQuantity::new(1_000_000_000); // Billion units
|
|
|
|
// All should be valid
|
|
assert_eq!(micro_position.to_i64(), 1);
|
|
assert_eq!(standard_position.to_i64(), 100_000);
|
|
assert_eq!(whale_position.to_i64(), 1_000_000_000);
|
|
|
|
// Operations should be safe
|
|
let _micro_double = micro_position.checked_multiply(2);
|
|
let _standard_double = standard_position.checked_multiply(2);
|
|
let whale_double = whale_position.checked_multiply(2);
|
|
|
|
// Whale position doubling might overflow
|
|
if whale_double.is_err() {
|
|
// This is expected and safe behavior
|
|
assert!(true);
|
|
} else {
|
|
// If it succeeds, result should be valid
|
|
assert!(whale_double.unwrap().to_i64() > 0);
|
|
}
|
|
} |