#[test]
fn test_chat_config_default() {
let config = ChatConfig::default();
assert!((config.temperature - 0.7).abs() < 0.01);
assert!((config.top_p - 0.9).abs() < 0.01);
assert_eq!(config.max_tokens, 512);
assert!(config.system.is_none());
assert!(!config.inspect);
assert!(
!config.force_cpu,
"F-GPU-134b: force_cpu should default to false"
);
}
#[test]
fn test_chat_config_with_system_prompt() {
let config = ChatConfig {
system: Some("You are a helpful assistant.".to_string()),
..Default::default()
};
assert!(config.system.is_some());
assert_eq!(
config.system.as_ref().expect("as_ref("),
"You are a helpful assistant."
);
}
#[test]
fn test_chat_config_trace_settings() {
let config = ChatConfig {
trace: true,
trace_output: Some(PathBuf::from("/tmp/trace.json")),
..Default::default()
};
assert!(config.trace);
assert_eq!(
config.trace_output.as_ref().expect("as_ref(").to_str().expect("to_str("),
"/tmp/trace.json"
);
}
#[test]
fn test_chat_config_force_cpu() {
let config = ChatConfig {
force_cpu: true,
..Default::default()
};
assert!(config.force_cpu);
}
#[test]
fn test_clean_chat_response_removes_chatml_markers() {
let raw = "<|im_start|>assistant\nHello world<|im_end|>";
let cleaned = clean_chat_response(raw);
assert_eq!(cleaned, "Hello world");
}
#[test]
fn test_clean_chat_response_removes_bpe_artifacts() {
let raw = "HelloĠworld";
let cleaned = clean_chat_response(raw);
assert_eq!(cleaned, "Hello world");
}
#[test]
fn test_clean_chat_response_normalizes_repeated_punctuation() {
let raw = "Wow!!!!!";
let cleaned = clean_chat_response(raw);
assert_eq!(cleaned, "Wow!!!");
let raw2 = "Really??????";
let cleaned2 = clean_chat_response(raw2);
assert_eq!(cleaned2, "Really???");
}
#[test]
fn test_clean_chat_response_normalizes_spaces() {
let raw = "Hello world";
let cleaned = clean_chat_response(raw);
assert_eq!(cleaned, "Hello world");
}
#[test]
fn test_clean_chat_response_trims_whitespace() {
let raw = " Hello world ";
let cleaned = clean_chat_response(raw);
assert_eq!(cleaned, "Hello world");
}
#[test]
fn test_clean_chat_response_removes_endoftext() {
let raw = "Hello<|endoftext|>world";
let cleaned = clean_chat_response(raw);
assert_eq!(cleaned, "Helloworld");
}
#[test]
fn test_clean_chat_response_newline_artifact() {
let raw = "HelloĊworld";
let cleaned = clean_chat_response(raw);
assert_eq!(cleaned, "Hello\nworld");
}
#[test]
fn test_clean_chat_response_strips_new_turn() {
let raw = "4\nSuggest a fun way to learn Rust";
let cleaned = clean_chat_response(raw);
assert_eq!(cleaned, "4");
}
#[test]
fn test_clean_chat_response_keeps_multiline_answer() {
let raw = "Here is the answer:\nLine 1\nLine 2";
let cleaned = clean_chat_response(raw);
assert_eq!(cleaned, "Here is the answer:\nLine 1\nLine 2");
}
#[test]
fn test_clean_chat_response_empty_string() {
let raw = "";
let cleaned = clean_chat_response(raw);
assert_eq!(cleaned, "");
}
#[test]
fn test_clean_chat_response_only_markers() {
let raw = "<|im_start|>assistant<|im_end|>";
let cleaned = clean_chat_response(raw);
assert_eq!(cleaned, "");
}
#[test]
fn test_clean_chat_response_human_prompt_cutoff() {
let raw = "Yes\nHuman: What else?";
let cleaned = clean_chat_response(raw);
assert_eq!(cleaned, "Yes");
}
#[test]
fn test_model_format_equality() {
assert_eq!(ModelFormat::Apr, ModelFormat::Apr);
assert_eq!(ModelFormat::Gguf, ModelFormat::Gguf);
assert_eq!(ModelFormat::SafeTensors, ModelFormat::SafeTensors);
assert_eq!(ModelFormat::Demo, ModelFormat::Demo);
}
#[test]
fn test_model_format_inequality() {
assert_ne!(ModelFormat::Apr, ModelFormat::Gguf);
assert_ne!(ModelFormat::Gguf, ModelFormat::SafeTensors);
assert_ne!(ModelFormat::SafeTensors, ModelFormat::Demo);
}
#[test]
fn test_model_format_debug() {
assert_eq!(format!("{:?}", ModelFormat::Apr), "Apr");
assert_eq!(format!("{:?}", ModelFormat::Gguf), "Gguf");
assert_eq!(format!("{:?}", ModelFormat::SafeTensors), "SafeTensors");
assert_eq!(format!("{:?}", ModelFormat::Demo), "Demo");
}
#[test]
fn test_model_format_clone() {
let format = ModelFormat::Apr;
let cloned = format;
assert_eq!(format, cloned);
}
#[test]
fn test_model_format_copy() {
let format = ModelFormat::Gguf;
let copied: ModelFormat = format;
assert_eq!(format, copied);
}
#[test]
fn test_detect_format_apr() {
let path = Path::new("/models/test.apr");
assert_eq!(detect_format(path), ModelFormat::Apr);
}
#[test]
fn test_detect_format_gguf() {
let path = Path::new("/models/test.gguf");
assert_eq!(detect_format(path), ModelFormat::Gguf);
}
#[test]
fn test_detect_format_safetensors() {
let path = Path::new("/models/model.safetensors");
assert_eq!(detect_format(path), ModelFormat::SafeTensors);
}
#[test]
fn test_detect_format_unknown_fallback_to_demo() {
let path = Path::new("/models/test.bin");
assert_eq!(detect_format(path), ModelFormat::Demo);
}
#[test]
fn test_detect_format_no_extension() {
let path = Path::new("/models/modelfile");
assert_eq!(detect_format(path), ModelFormat::Demo);
}
#[test]
fn test_detect_format_case_sensitive() {
let path = Path::new("/models/test.APR");
assert_eq!(detect_format(path), ModelFormat::Demo);
}
#[test]
fn test_detect_format_nested_path() {
let path = Path::new("/home/user/.cache/models/qwen2-0.5b.gguf");
assert_eq!(detect_format(path), ModelFormat::Gguf);
}
#[test]
fn test_detect_format_relative_path() {
let path = Path::new("./models/model.safetensors");
assert_eq!(detect_format(path), ModelFormat::SafeTensors);
}
#[cfg(feature = "inference")]
mod inference_tests {
use super::*;
#[test]
fn test_detect_format_from_bytes_apr_v1() {
let data = b"APRNxxxx00000000";
assert_eq!(detect_format_from_bytes(data), ModelFormat::Apr);
}
#[test]
fn test_detect_format_from_bytes_apr_v2() {
let data = b"APR2xxxx00000000";
assert_eq!(detect_format_from_bytes(data), ModelFormat::Apr);
}
#[test]
fn test_detect_format_from_bytes_apr_null() {
let data = b"APR\0xxxx00000000";
assert_eq!(detect_format_from_bytes(data), ModelFormat::Apr);
}
#[test]
fn test_detect_format_from_bytes_gguf() {
let data = b"GGUFxxxx00000000";
assert_eq!(detect_format_from_bytes(data), ModelFormat::Gguf);
}
#[test]
fn test_detect_format_from_bytes_safetensors() {
let mut data = vec![0u8; 16];
data[0..8].copy_from_slice(&1000u64.to_le_bytes());
assert_eq!(detect_format_from_bytes(&data), ModelFormat::SafeTensors);
}
#[test]
fn test_detect_format_from_bytes_too_short() {
let data = b"APR";
assert_eq!(detect_format_from_bytes(data), ModelFormat::Demo);
}
#[test]
fn test_detect_format_from_bytes_empty() {
let data: &[u8] = &[];
assert_eq!(detect_format_from_bytes(data), ModelFormat::Demo);
}
#[test]
fn test_detect_format_from_bytes_unknown_magic() {
let data = b"UNKN0000\x00\x00\x00\x00\x00\x00\x00\x00";
assert_eq!(detect_format_from_bytes(data), ModelFormat::Demo);
}
}
#[test]
fn test_run_file_not_found() {
let path = Path::new("/nonexistent/model.gguf");
let result = run(
path, 0.7, 0.9, 512, None, false, false, false, None, false, None, "info", false,
);
assert!(result.is_err());
match result {
Err(CliError::FileNotFound(p)) => {
assert_eq!(p, PathBuf::from("/nonexistent/model.gguf"));
}
other => panic!("Expected FileNotFound error, got {:?}", other),
}
}
#[test]
fn test_run_with_trace_config() {
let path = Path::new("/nonexistent/model.gguf");
let result = run(
path,
0.7,
0.9,
512,
Some("You are helpful"),
true,
true,
true,
Some(&["tokenize".to_string(), "sample".to_string()]),
true,
Some(PathBuf::from("/tmp/trace.json")),
"debug",
true,
);
assert!(result.is_err());
}
#[test]
fn test_command_result_variants() {
let continue_result = CommandResult::Continue;
let quit_result = CommandResult::Quit;
match continue_result {
CommandResult::Continue => {}
CommandResult::Quit => panic!("Expected Continue"),
}
match quit_result {
CommandResult::Quit => {}
CommandResult::Continue => panic!("Expected Quit"),
}
}
#[test]
fn test_clean_chat_response_mixed_markers() {
let raw = "<|im_start|>assistant\n<|im_start|>Hello<|im_end|><|endoftext|>";
let cleaned = clean_chat_response(raw);
assert_eq!(cleaned, "Hello");
}
#[test]
fn test_clean_chat_response_repeated_dots() {
let raw = "Hmm........ let me think";
let cleaned = clean_chat_response(raw);
assert_eq!(cleaned, "Hmm... let me think");
}
#[test]
fn test_clean_chat_response_unicode_preserved() {
let raw = "こんにちは世界";
let cleaned = clean_chat_response(raw);
assert_eq!(cleaned, "こんにちは世界");
}
#[test]
fn test_clean_chat_response_emoji_preserved() {
let raw = "Hello 👋 World 🌍";
let cleaned = clean_chat_response(raw);
assert_eq!(cleaned, "Hello 👋 World 🌍");
}
#[test]
fn test_clean_chat_response_code_block() {
let raw = "Here is code:\n```rust\nfn main() {}\n```";
let cleaned = clean_chat_response(raw);
assert_eq!(cleaned, "Here is code:\n```rust\nfn main() {}\n```");
}
#[test]
fn test_clean_chat_response_numbered_list() {
let raw = "Steps:\n1. First\n2. Second\n3. Third";
let cleaned = clean_chat_response(raw);
assert_eq!(cleaned, "Steps:\n1. First\n2. Second\n3. Third");
}
#[cfg(not(feature = "inference"))]
#[test]
fn test_format_params_billions() {
assert_eq!(format_params(7_000_000_000), "7.0B");
assert_eq!(format_params(1_500_000_000), "1.5B");
}
#[cfg(not(feature = "inference"))]
#[test]
fn test_format_params_millions() {
assert_eq!(format_params(500_000_000), "500.0M");
assert_eq!(format_params(7_000_000), "7.0M");
assert_eq!(format_params(1_500_000), "1.5M");
}
#[cfg(not(feature = "inference"))]
#[test]
fn test_format_params_thousands() {
assert_eq!(format_params(500_000), "500.0K");
assert_eq!(format_params(7_000), "7.0K");
assert_eq!(format_params(1_500), "1.5K");
}
#[cfg(not(feature = "inference"))]
#[test]
fn test_format_params_small() {
assert_eq!(format_params(999), "999");
assert_eq!(format_params(100), "100");
assert_eq!(format_params(1), "1");
assert_eq!(format_params(0), "0");
}