use model_rs::{error::ModelError, validation::*};
use std::path::Path;
use std::path::PathBuf;
use tokio_test;
#[tokio::test]
async fn test_error_handling_edge_cases() {
let err = ModelError::download_failed("network timeout");
assert!(err.to_string().contains("Download failed"));
let err = ModelError::model_not_found("missing/model");
assert!(err.to_string().contains("Model not found"));
let err = ModelError::invalid_config("invalid value");
assert!(err.to_string().contains("Invalid configuration"));
}
#[tokio::test]
async fn test_validation_edge_cases() {
let validator = &*MODEL_VALIDATOR;
let long_name = "a".repeat(1000);
let result = validator.validate_model_name(&format!("org/{}", long_name));
assert!(result.is_ok());
for valid_name in [
"test_org/model_name",
"test-org/model-name",
"test_org/model_v2",
"test-org/model.v2",
] {
assert!(validator.validate_model_name(valid_name).is_ok());
}
for invalid_name in [
"", "no_slash", "/model", "org//model", "org/model/", "org\\model", ] {
assert!(validator.validate_model_name(invalid_name).is_err());
}
}
#[tokio::test]
async fn test_http_validation_edge_cases() {
assert!(validate_port(0).is_err()); assert!(validate_port(65535).is_ok()); assert!(validate_port(8080).is_ok()); assert!(validate_port(1).is_ok()); assert!(validate_port(65535).is_ok());
assert!(validate_max_tokens(0).is_err()); assert!(validate_max_tokens(50000).is_err()); assert!(validate_max_tokens(1000).is_ok()); assert!(validate_max_tokens(1).is_ok()); assert!(validate_max_tokens(32768).is_ok());
assert!(validate_temperature(-0.1).is_err()); assert!(validate_temperature(2.1).is_err()); assert!(validate_temperature(0.0).is_ok()); assert!(validate_temperature(1.0).is_ok()); assert!(validate_temperature(2.0).is_ok());
assert!(validate_top_p(-0.1).is_err()); assert!(validate_top_p(1.1).is_err()); assert!(validate_top_p(0.0).is_ok()); assert!(validate_top_p(0.5).is_ok()); assert!(validate_top_p(1.0).is_ok());
assert!(validate_top_k(0).is_err()); assert!(validate_top_k(1001).is_err()); assert!(validate_top_k(1).is_ok()); assert!(validate_top_k(100).is_ok()); assert!(validate_top_k(1000).is_ok());
assert!(validate_repeat_penalty(-0.1).is_err()); assert!(validate_repeat_penalty(2.1).is_err()); assert!(validate_repeat_penalty(0.0).is_ok()); assert!(validate_repeat_penalty(1.0).is_ok()); assert!(validate_repeat_penalty(2.0).is_ok()); }
#[tokio::test]
async fn test_device_validation_edge_cases() {
for valid_device in ["auto", "cpu", "metal", "cuda", "mlx"] {
assert!(DEVICE_VALIDATOR.validate_device(valid_device).is_ok());
}
for invalid_device in [
"", "invalid_device", "CPU", " metal", "metal ", ] {
assert!(DEVICE_VALIDATOR.validate_device(invalid_device).is_err());
}
assert!(DEVICE_VALIDATOR.validate_device_index(Some(8)).is_err()); assert!(DEVICE_VALIDATOR.validate_device_index(Some(0)).is_ok()); assert!(DEVICE_VALIDATOR.validate_device_index(Some(7)).is_ok()); assert!(DEVICE_VALIDATOR.validate_device_index(None).is_ok()); }
#[tokio::test]
async fn test_path_validation_edge_cases() {
let validator = &*MODEL_VALIDATOR;
for valid_path in [
"/path/to/model",
"./relative/path",
"/Users/user/model",
"/opt/models/test-model",
"/path/with-dashes/and.dots",
"./path_with_underscores",
"/very/long/path/that/goes/through/multiple/directories",
] {
assert!(validator.validate_path(Path::new(valid_path), "test").is_ok());
}
for invalid_path in [
"", "/path/../to/model", "~/model", "../../etc/passwd", ] {
assert!(validator.validate_path(Path::new(invalid_path), "test").is_err());
}
}
#[tokio::test]
async fn test_model_directory_validation_edge_cases() {
let validator = &*MODEL_VALIDATOR;
let non_existent = PathBuf::from("/tmp/non_existent_model_12345");
let result = validator.validate_model_directory(&non_existent).await;
assert!(result.is_err());
}
#[tokio::test]
async fn test_error_conversion_edge_cases() {
let io_err = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "permission denied");
let model_err: ModelError = io_err.into();
assert!(model_err.to_string().contains("permission denied"));
let json_err = serde_json::from_str::<serde_json::Value>("invalid { json").unwrap_err();
let model_err: ModelError = json_err.into();
assert!(model_err.to_string().contains("JSON error"));
}
#[tokio::test]
async fn test_cli_argument_validation_integration() {
assert!(validate_model_name("meta-llama/Llama-2-7b").is_ok());
assert!(validate_model_name("TinyLlama/TinyLlama-1.1B-Chat-v1.0").is_ok());
assert!(validate_model_name("").is_err());
assert!(validate_model_name("invalid_model_name").is_err());
assert!(validate_model_name("org//model").is_err());
assert!(validate_port(8080).is_ok());
assert!(validate_port(0).is_err());
assert!(validate_port(65535).is_ok());
assert!(validate_temperature(0.7).is_ok());
assert!(validate_temperature(-0.1).is_err());
assert!(validate_temperature(2.1).is_err());
}
#[tokio::test]
async fn test_configuration_validation_edge_cases() {
let config_tests = vec![
(Some("".to_string()), "empty port"),
(Some("99999".to_string()), "invalid port number"),
(Some("0".to_string()), "port zero"),
(Some("65536".to_string()), "port too high"),
(Some("1.5".to_string()), "non-integer port"),
];
for (value, description) in config_tests {
match value {
Some(port_str) => {
if let Ok(port) = port_str.parse::<u16>() {
let result = validate_port(port);
match result {
Ok(_) => println!("Port {} validation passed: {}", port_str, description),
Err(e) => println!("Port {} validation failed: {} - {}", port_str, description, e),
}
} else {
println!("Port {} invalid format: {}", port_str, description);
}
}
None => println!("Missing configuration: {}", description),
}
}
}
#[tokio::test]
async fn test_network_error_scenarios() {
let error_scenarios = vec![
("Connection timeout", "Connection timed out"),
("DNS resolution failed", "failed to resolve"),
];
for (scenario, error_msg) in error_scenarios {
println!("Testing network error scenario: {}", scenario);
assert!(!error_msg.is_empty());
}
}
#[tokio::test]
async fn test_filesystem_error_scenarios() {
let error_scenarios = vec![
("Permission denied", "Permission denied"),
("Disk full", "No space left on device"),
("File not found", "No such file or directory"),
("Invalid path", "Invalid filename"),
];
for (scenario, error_msg) in error_scenarios {
println!("Testing filesystem error scenario: {}", scenario);
assert!(!error_msg.is_empty());
}
}
#[tokio::test]
async fn test_memory_error_scenarios() {
let memory_scenarios = vec![
("Out of memory", "out of memory"),
("Allocation failed", "allocation failed"),
("Buffer too large", "buffer too large"),
];
for (scenario, error_msg) in memory_scenarios {
println!("Testing memory error scenario: {}", scenario);
assert!(!error_msg.is_empty());
}
}
#[tokio::test]
async fn test_concurrent_validation() {
use tokio::task;
let mut handles = vec![];
for i in 0..10 {
let handle = task::spawn(async move {
let model_name = format!("test-org/model-{}", i);
validate_model_name(&model_name)
});
handles.push(handle);
}
for handle in handles {
let result = handle.await.unwrap();
assert!(result.is_ok());
}
}
#[tokio::test]
async fn test_validation_error_messages() {
let test_cases: Vec<(Box<dyn Fn() -> model_rs::Result<()>>, &str)> = vec![
(Box::new(|| validate_model_name("")), "empty model name"),
(Box::new(|| validate_model_name("no_slash")), "missing organization"),
(Box::new(|| validate_model_name("org//model")), "invalid format"),
(Box::new(|| validate_port(0)), "invalid port"),
(Box::new(|| validate_max_tokens(0)), "invalid max tokens"),
(Box::new(|| validate_temperature(-0.1)), "invalid temperature"),
(Box::new(|| validate_device("invalid_device")), "invalid device"),
];
for (validation_fn, description) in test_cases {
let result = validation_fn();
assert!(result.is_err(), "Validation should fail for: {}", description);
let error = result.unwrap_err();
let error_str = error.to_string();
assert!(!error_str.is_empty());
assert!(error_str.len() > 10, "Error message should be descriptive");
}
}
#[tokio::test]
async fn test_result_type_alias_comprehensive() {
type TestResult<T> = std::result::Result<T, ModelError>;
let ok_result: TestResult<String> = Ok("test".to_string());
assert!(ok_result.is_ok());
assert_eq!(ok_result.unwrap(), "test");
let err_result: TestResult<String> = Err(ModelError::validation_error(
"test",
"validation failed",
"fix input"
));
assert!(err_result.is_err());
let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
let model_error: ModelError = io_error.into();
let result: TestResult<()> = Err(model_error);
assert!(result.is_err());
if let Err(e) = result {
assert!(e.to_string().contains("file not found"));
}
}
#[tokio::test]
async fn test_error_aggregation_patterns() {
let mut errors = Vec::new();
let validations: [std::result::Result<(), ModelError>; 4] = [
validate_model_name(""),
validate_port(0),
validate_temperature(5.0),
validate_device("invalid"),
];
for validation in validations {
if let Err(e) = validation {
errors.push(e);
}
}
assert!(errors.len() >= 2);
let error_strings: Vec<String> = errors.iter().map(|e| e.to_string()).collect();
let unique_errors: Vec<String> = error_strings.iter().cloned().collect();
assert_eq!(error_strings.len(), unique_errors.len()); }