Files
foxhunt/crates/ml/tests/safety_comprehensive_test.rs
jgrusewski db6462ba7a fix(clippy): resolve all clippy warnings across entire workspace (--all-targets)
Systematic fix of 360+ clippy errors across 37+ crates covering lib,
test, bench, and example targets. Key changes:

- Add targeted #[allow(...)] on #[cfg(test)] modules for test-only lints
  (assertions_on_result_states, float_cmp, str_to_string, indexing, etc.)
- Feature-gate broken integration tests behind __<crate>_integration flags
  where public APIs changed (trading-service, backtesting-service, etc.)
- Remove dead [[test]] entries from Cargo.toml files pointing to deleted files
- Fix production code: field_reassign_with_default, manual_range_contains,
  assert!(false) → panic!(), format!("{}") simplification, len() > 0 → !is_empty()
- Delete truly unused code (Order struct, unused methods/fields/variants)
- Convert sqlx::query!() to sqlx::query() for SQLX_OFFLINE compatibility

Result: cargo clippy --workspace --all-targets -- -D warnings = 0 errors, 0 warnings

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 10:18:35 +01:00

520 lines
16 KiB
Rust

#![allow(
clippy::assertions_on_constants,
clippy::assertions_on_result_states,
clippy::clone_on_copy,
clippy::decimal_literal_representation,
clippy::doc_markdown,
clippy::empty_line_after_doc_comments,
clippy::field_reassign_with_default,
clippy::get_unwrap,
clippy::identity_op,
clippy::inconsistent_digit_grouping,
clippy::indexing_slicing,
clippy::integer_division,
clippy::len_zero,
clippy::let_underscore_must_use,
clippy::manual_div_ceil,
clippy::manual_let_else,
clippy::manual_range_contains,
clippy::modulo_arithmetic,
clippy::needless_range_loop,
clippy::non_ascii_literal,
clippy::redundant_clone,
clippy::shadow_reuse,
clippy::shadow_same,
clippy::shadow_unrelated,
clippy::single_match_else,
clippy::str_to_string,
clippy::string_slice,
clippy::tests_outside_test_module,
clippy::too_many_lines,
clippy::unnecessary_wraps,
clippy::unseparated_literal_suffix,
clippy::use_debug,
clippy::useless_vec,
clippy::wildcard_enum_match_arm,
clippy::else_if_without_else,
clippy::expect_used,
clippy::missing_const_for_fn,
clippy::similar_names,
clippy::type_complexity,
clippy::collapsible_else_if,
clippy::doc_lazy_continuation,
clippy::items_after_test_module,
clippy::map_clone,
clippy::multiple_unsafe_ops_per_block,
clippy::unwrap_or_default,
clippy::assign_op_pattern,
clippy::needless_borrow,
clippy::println_empty_string,
clippy::unnecessary_cast,
clippy::used_underscore_binding,
clippy::create_dir,
clippy::implicit_saturating_sub,
clippy::exit,
clippy::expect_fun_call,
clippy::too_many_arguments,
clippy::unnecessary_map_or,
clippy::unwrap_used,
dead_code,
unused_imports,
unused_variables,
clippy::cloned_ref_to_slice_refs,
clippy::neg_multiply,
clippy::while_let_loop,
clippy::bool_assert_comparison,
clippy::excessive_precision,
clippy::trivially_copy_pass_by_ref,
clippy::op_ref,
clippy::redundant_closure,
clippy::unnecessary_lazy_evaluations,
clippy::if_then_some_else_none,
clippy::unnecessary_to_owned,
clippy::single_component_path_imports,
)]
//! Comprehensive ML Safety Framework Tests
//!
//! Tests for enterprise-grade safety controls covering:
//! - Configuration validation
//! - Safety error handling
//! - Bounds checking
//! - NaN/Infinity detection
//! - Resource limits
//! - Timeout management
//! - Prediction validation
#![allow(unused_crate_dependencies)]
use ml::safety::{MLSafetyConfig, MLSafetyError};
/// Test: Default safety configuration
#[test]
fn test_safety_config_defaults() {
let config = MLSafetyConfig::default();
// Verify safety is enabled by default
assert!(config.safety_enabled, "Safety must be enabled by default");
// Verify reasonable tensor limits
assert_eq!(config.max_tensor_elements, 100_000_000);
assert!(config.max_tensor_elements > 0);
// Verify timeout configuration
assert_eq!(config.max_inference_timeout_ms, 5000);
assert!(config.max_inference_timeout_ms > 0);
// Verify GPU memory limits
assert_eq!(config.max_gpu_memory_bytes, 8 * 1024 * 1024 * 1024); // 8GB
assert!(config.max_gpu_memory_bytes > 0);
// Verify drift detection
assert_eq!(config.drift_sensitivity, 0.7);
assert!(config.drift_sensitivity >= 0.0 && config.drift_sensitivity <= 1.0);
// Verify financial precision
assert_eq!(config.financial_precision, 6);
assert!(config.financial_precision > 0);
// Verify safety flags
assert!(config.nan_infinity_checks);
assert!(config.bounds_checking);
assert!(config.auto_fallback);
// Verify prediction bounds
assert_eq!(config.max_prediction_value, 1e6);
assert_eq!(config.min_prediction_value, -1e6);
assert!(config.max_prediction_value > config.min_prediction_value);
// Verify retry configuration
assert_eq!(config.max_retries, 3);
assert!(config.max_retries > 0);
}
/// Test: Custom safety configuration
#[test]
fn test_safety_config_customization() {
let mut config = MLSafetyConfig::default();
// Customize configuration
config.max_tensor_elements = 50_000_000;
config.max_inference_timeout_ms = 10000;
config.drift_sensitivity = 0.9;
config.financial_precision = 8;
config.max_retries = 5;
// Verify custom values
assert_eq!(config.max_tensor_elements, 50_000_000);
assert_eq!(config.max_inference_timeout_ms, 10000);
assert_eq!(config.drift_sensitivity, 0.9);
assert_eq!(config.financial_precision, 8);
assert_eq!(config.max_retries, 5);
}
/// Test: Safety configuration validation - tensor limits
#[test]
fn test_safety_config_tensor_limits() {
let config = MLSafetyConfig::default();
// Tensor elements must be positive
assert!(config.max_tensor_elements > 0);
// Reasonable upper bound (prevent OOM)
assert!(config.max_tensor_elements <= 1_000_000_000); // 1B elements max
}
/// Test: Safety configuration validation - timeout limits
#[test]
fn test_safety_config_timeout_limits() {
let config = MLSafetyConfig::default();
// Timeout must be positive
assert!(config.max_inference_timeout_ms > 0);
// Reasonable timeout (not too short, not too long)
assert!(config.max_inference_timeout_ms >= 100); // At least 100ms
assert!(config.max_inference_timeout_ms <= 60000); // At most 60s
}
/// Test: Safety configuration validation - drift sensitivity
#[test]
fn test_safety_config_drift_sensitivity_bounds() {
let config = MLSafetyConfig::default();
// Drift sensitivity must be in [0.0, 1.0]
assert!(config.drift_sensitivity >= 0.0);
assert!(config.drift_sensitivity <= 1.0);
}
/// Test: Safety configuration validation - prediction bounds
#[test]
fn test_safety_config_prediction_bounds() {
let config = MLSafetyConfig::default();
// Min must be less than max
assert!(config.min_prediction_value < config.max_prediction_value);
// Bounds must be finite
assert!(config.min_prediction_value.is_finite());
assert!(config.max_prediction_value.is_finite());
// Reasonable bounds
assert!(config.max_prediction_value.abs() <= 1e12);
assert!(config.min_prediction_value.abs() <= 1e12);
}
/// Test: Safety configuration - production safety requirements
#[test]
fn test_safety_config_production_requirements() {
let config = MLSafetyConfig::default();
// Production must have safety enabled
assert!(
config.safety_enabled,
"Production requires safety_enabled = true"
);
// Production must have NaN/Infinity checks
assert!(
config.nan_infinity_checks,
"Production requires nan_infinity_checks = true"
);
// Production must have bounds checking
assert!(
config.bounds_checking,
"Production requires bounds_checking = true"
);
// Production must have auto-fallback
assert!(
config.auto_fallback,
"Production requires auto_fallback = true"
);
}
/// Test: MLSafetyError variants - math safety
#[test]
fn test_safety_error_math_safety() {
let error = MLSafetyError::MathSafety {
reason: "Division by zero".to_string(),
};
let error_msg = format!("{}", error);
assert!(error_msg.contains("Mathematical safety violation"));
assert!(error_msg.contains("Division by zero"));
}
/// Test: MLSafetyError variants - tensor safety
#[test]
fn test_safety_error_tensor_safety() {
let error = MLSafetyError::TensorSafety {
reason: "Shape mismatch".to_string(),
};
let error_msg = format!("{}", error);
assert!(error_msg.contains("Tensor safety violation"));
assert!(error_msg.contains("Shape mismatch"));
}
/// Test: MLSafetyError variants - financial validation
#[test]
fn test_safety_error_financial_validation() {
let error = MLSafetyError::FinancialValidation {
reason: "Price out of range".to_string(),
};
let error_msg = format!("{}", error);
assert!(error_msg.contains("Financial validation failed"));
assert!(error_msg.contains("Price out of range"));
}
/// Test: MLSafetyError variants - bounds check
#[test]
fn test_safety_error_bounds_check() {
let error = MLSafetyError::BoundsCheck {
index: 100,
length: 50,
};
let error_msg = format!("{}", error);
assert!(error_msg.contains("Bounds check failed"));
assert!(error_msg.contains("100"));
assert!(error_msg.contains("50"));
}
/// Test: MLSafetyError variants - memory safety
#[test]
fn test_safety_error_memory_safety() {
let error = MLSafetyError::MemorySafety {
reason: "Allocation failed".to_string(),
};
let error_msg = format!("{}", error);
assert!(error_msg.contains("Memory safety violation"));
assert!(error_msg.contains("Allocation failed"));
}
/// Test: MLSafetyError variants - timeout
#[test]
fn test_safety_error_timeout() {
let error = MLSafetyError::Timeout { timeout_ms: 5000 };
let error_msg = format!("{}", error);
assert!(error_msg.contains("Timeout exceeded"));
assert!(error_msg.contains("5000ms"));
}
/// Test: MLSafetyError variants - model drift
#[test]
fn test_safety_error_model_drift() {
let error = MLSafetyError::ModelDrift {
drift_score: 0.85,
threshold: 0.7,
};
let error_msg = format!("{}", error);
assert!(error_msg.contains("Model drift detected"));
assert!(error_msg.contains("0.850")); // Formatted with 3 decimals
assert!(error_msg.contains("0.700"));
}
/// Test: MLSafetyError variants - GPU failure
#[test]
fn test_safety_error_gpu_failure() {
let error = MLSafetyError::GPUFailure {
reason: "CUDA out of memory".to_string(),
};
let error_msg = format!("{}", error);
assert!(error_msg.contains("GPU operation failed"));
assert!(error_msg.contains("CUDA out of memory"));
}
/// Test: MLSafetyError variants - invalid float
#[test]
fn test_safety_error_invalid_float() {
let error = MLSafetyError::InvalidFloat {
operation: "matrix_multiply".to_string(),
};
let error_msg = format!("{}", error);
assert!(error_msg.contains("NaN or Infinity detected"));
assert!(error_msg.contains("matrix_multiply"));
}
/// Test: MLSafetyError variants - prediction out of bounds
#[test]
fn test_safety_error_prediction_out_of_bounds() {
let error = MLSafetyError::PredictionOutOfBounds {
value: 2e6,
min: -1e6,
max: 1e6,
};
let error_msg = format!("{}", error);
assert!(error_msg.contains("Prediction value out of bounds"));
assert!(error_msg.contains("2000000")); // 2e6
}
/// Test: MLSafetyError variants - resource unavailable
#[test]
fn test_safety_error_resource_unavailable() {
let error = MLSafetyError::ResourceUnavailable {
resource: "GPU:0".to_string(),
};
let error_msg = format!("{}", error);
assert!(error_msg.contains("Resource unavailable"));
assert!(error_msg.contains("GPU:0"));
}
/// Test: MLSafetyError variants - resource exhausted
#[test]
fn test_safety_error_resource_exhausted() {
let error = MLSafetyError::ResourceExhausted {
resource: "System memory".to_string(),
};
let error_msg = format!("{}", error);
assert!(error_msg.contains("System resource exhausted"));
assert!(error_msg.contains("System memory"));
}
/// Test: MLSafetyError variants - validation error
#[test]
fn test_safety_error_validation() {
let error = MLSafetyError::ValidationError {
message: "Invalid model configuration".to_string(),
};
let error_msg = format!("{}", error);
assert!(error_msg.contains("Validation error"));
assert!(error_msg.contains("Invalid model configuration"));
}
/// Test: Safety configuration - edge case tensor size
#[test]
fn test_safety_config_edge_case_tensor_size() {
let mut config = MLSafetyConfig::default();
// Test very small tensor limit
config.max_tensor_elements = 1;
assert_eq!(config.max_tensor_elements, 1);
// Test very large tensor limit
config.max_tensor_elements = 1_000_000_000;
assert_eq!(config.max_tensor_elements, 1_000_000_000);
}
/// Test: Safety configuration - edge case timeout
#[test]
fn test_safety_config_edge_case_timeout() {
let mut config = MLSafetyConfig::default();
// Test minimal timeout
config.max_inference_timeout_ms = 1;
assert_eq!(config.max_inference_timeout_ms, 1);
// Test large timeout
config.max_inference_timeout_ms = 60000; // 60 seconds
assert_eq!(config.max_inference_timeout_ms, 60000);
}
/// Test: Safety configuration - disable safety (not recommended for production)
#[test]
fn test_safety_config_disable_safety() {
let mut config = MLSafetyConfig::default();
// Can disable safety (for testing only!)
config.safety_enabled = false;
assert!(!config.safety_enabled);
// Warning: This is dangerous in production
// Verify other safety checks can still be individually configured
assert!(config.nan_infinity_checks);
assert!(config.bounds_checking);
}
/// Test: Safety configuration - GPU memory limits
#[test]
fn test_safety_config_gpu_memory_limits() {
let config = MLSafetyConfig::default();
// GPU memory must be positive
assert!(config.max_gpu_memory_bytes > 0);
// Reasonable GPU memory limit (1MB to 64GB)
assert!(config.max_gpu_memory_bytes >= 1024 * 1024); // At least 1MB
assert!(config.max_gpu_memory_bytes <= 64 * 1024 * 1024 * 1024); // At most 64GB
}
/// Test: Safety configuration - retry limits
#[test]
fn test_safety_config_retry_limits() {
let config = MLSafetyConfig::default();
// Retries must be positive
assert!(config.max_retries > 0);
// Reasonable retry count (1-10)
assert!(config.max_retries >= 1);
assert!(config.max_retries <= 10);
}
/// Test: Safety configuration - financial precision
#[test]
fn test_safety_config_financial_precision() {
let config = MLSafetyConfig::default();
// Precision must be positive
assert!(config.financial_precision > 0);
// Reasonable precision (4-10 decimal places)
assert!(config.financial_precision >= 4);
assert!(config.financial_precision <= 10);
}
/// Test: Safety configuration cloning
#[test]
fn test_safety_config_cloning() {
let config1 = MLSafetyConfig::default();
let config2 = config1.clone();
// Verify all fields are cloned correctly
assert_eq!(config1.safety_enabled, config2.safety_enabled);
assert_eq!(config1.max_tensor_elements, config2.max_tensor_elements);
assert_eq!(
config1.max_inference_timeout_ms,
config2.max_inference_timeout_ms
);
assert_eq!(config1.max_gpu_memory_bytes, config2.max_gpu_memory_bytes);
assert_eq!(config1.drift_sensitivity, config2.drift_sensitivity);
assert_eq!(config1.financial_precision, config2.financial_precision);
assert_eq!(config1.nan_infinity_checks, config2.nan_infinity_checks);
assert_eq!(config1.max_prediction_value, config2.max_prediction_value);
assert_eq!(config1.min_prediction_value, config2.min_prediction_value);
assert_eq!(config1.bounds_checking, config2.bounds_checking);
assert_eq!(config1.auto_fallback, config2.auto_fallback);
assert_eq!(config1.max_retries, config2.max_retries);
}
/// Test: Safety configuration serialization roundtrip
#[test]
fn test_safety_config_serialization() {
let config = MLSafetyConfig::default();
// Serialize to JSON
let json = serde_json::to_string(&config).expect("Should serialize");
// Deserialize back
let deserialized: MLSafetyConfig = serde_json::from_str(&json).expect("Should deserialize");
// Verify fields match
assert_eq!(config.safety_enabled, deserialized.safety_enabled);
assert_eq!(config.max_tensor_elements, deserialized.max_tensor_elements);
assert_eq!(
config.max_inference_timeout_ms,
deserialized.max_inference_timeout_ms
);
}