#[test]
fn test_command_result_continue_is_not_quit() {
let result = CommandResult::Continue;
let is_continue = matches!(result, CommandResult::Continue);
assert!(is_continue);
}
#[test]
fn test_command_result_quit_is_not_continue() {
let result = CommandResult::Quit;
let is_quit = matches!(result, CommandResult::Quit);
assert!(is_quit);
}
#[test]
fn test_find_qwen_tokenizer_nonexistent_path() {
let path = Path::new("/nonexistent/deeply/nested/model.safetensors");
let result = find_qwen_tokenizer(path);
match result {
Ok(Some(tok)) => {
assert!(tok.vocab_size() > 0);
}
Ok(None) => {
panic!("Expected Err or Ok(Some), got Ok(None)");
}
Err(CliError::InvalidFormat(msg)) => {
assert!(
msg.contains("No Qwen tokenizer found"),
"Expected helpful error message, got: {}",
msg
);
}
Err(other) => panic!("Expected InvalidFormat error, got: {:?}", other),
}
}
#[test]
fn test_find_qwen_tokenizer_root_path() {
let path = Path::new("/model.safetensors");
let result = find_qwen_tokenizer(path);
assert!(result.is_ok() || result.is_err());
}
#[test]
fn test_find_qwen_tokenizer_error_message_content_when_no_cache() {
let err = CliError::InvalidFormat(
"No Qwen tokenizer found. Searched:\n\
1. Model directory (tokenizer.json)\n\
2. HuggingFace cache (~/.cache/huggingface/hub/models--Qwen--*/snapshots/*/tokenizer.json)\n\
3. APR cache (~/.apr/tokenizers/qwen2/tokenizer.json)\n\n\
To fix: Download a Qwen model with tokenizer:\n\
apr pull hf://Qwen/Qwen2.5-0.5B-Instruct-GGUF"
.to_string(),
);
let msg = err.to_string();
assert!(msg.contains("No Qwen tokenizer found"));
assert!(msg.contains("tokenizer.json"));
assert!(msg.contains("HuggingFace cache"));
assert!(msg.contains("APR cache"));
assert!(msg.contains("apr pull"));
}
#[test]
fn test_find_qwen_tokenizer_searches_parent_directory_first() {
let path = Path::new("/tmp/no_tokenizer_here/model.safetensors");
let result = find_qwen_tokenizer(path);
let _ = result;
}
#[test]
fn test_clean_chat_response_full_chatml_response() {
let raw = "<|im_start|>assistant\nThe answer is 42.<|im_end|><|endoftext|>";
let cleaned = clean_chat_response(raw);
assert_eq!(cleaned, "The answer is 42.");
}
#[test]
fn test_clean_chat_response_bpe_with_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_complex_combined() {
let raw = "<|im_start|>assistant\nÄ Ä Hello!!!!!!Ä Ä world<|im_end|><|endoftext|>";
let cleaned = clean_chat_response(raw);
assert_eq!(cleaned, "Hello!!! world");
}
#[test]
fn test_clean_chat_response_very_long_input() {
let raw = "x".repeat(100_000);
let cleaned = clean_chat_response(&raw);
assert_eq!(cleaned.len(), 100_000);
}
#[test]
fn test_clean_chat_response_only_bpe_artifacts() {
let raw = "Ä Ä Ä ";
let cleaned = clean_chat_response(raw);
assert!(cleaned.is_empty());
}
#[test]
fn test_clean_chat_response_marker_in_middle_of_word() {
let raw = "hel<|im_end|>lo";
let cleaned = clean_chat_response(raw);
assert_eq!(cleaned, "hello");
}
#[test]
fn test_clean_chat_response_multiple_im_start_assistant() {
let raw = "<|im_start|>assistant\n<|im_start|>assistant\nHello";
let cleaned = clean_chat_response(raw);
assert_eq!(cleaned, "Hello");
}
#[test]
fn test_clean_chat_response_newline_bpe_and_human_cutoff() {
let raw = "DoneÄŠHuman: next question";
let cleaned = clean_chat_response(raw);
assert_eq!(cleaned, "Done");
}
#[test]
fn test_clean_chat_response_single_char() {
let raw = "a";
let cleaned = clean_chat_response(raw);
assert_eq!(cleaned, "a");
}
#[test]
fn test_clean_chat_response_just_newline() {
let raw = "\n";
let cleaned = clean_chat_response(raw);
assert!(cleaned.is_empty());
}
#[test]
fn test_clean_chat_response_just_markers() {
let raw = "<|im_start|><|im_end|><|endoftext|>";
let cleaned = clean_chat_response(raw);
assert!(cleaned.is_empty());
}
#[test]
fn test_model_format_debug_format_all() {
let variants = [
(ModelFormat::Apr, "Apr"),
(ModelFormat::Gguf, "Gguf"),
(ModelFormat::SafeTensors, "SafeTensors"),
(ModelFormat::Demo, "Demo"),
];
for (variant, expected) in variants {
assert_eq!(format!("{:?}", variant), expected);
}
}
#[test]
fn test_model_format_clone_all_variants() {
let variants = [
ModelFormat::Apr,
ModelFormat::Gguf,
ModelFormat::SafeTensors,
ModelFormat::Demo,
];
for variant in variants {
let cloned = variant;
assert_eq!(variant, cloned);
}
}
#[test]
fn test_model_format_eq_reflexive() {
let formats = [
ModelFormat::Apr,
ModelFormat::Gguf,
ModelFormat::SafeTensors,
ModelFormat::Demo,
];
for f in formats {
assert_eq!(f, f);
}
}
#[test]
fn test_model_format_ne_all_pairs() {
let formats = [
ModelFormat::Apr,
ModelFormat::Gguf,
ModelFormat::SafeTensors,
ModelFormat::Demo,
];
for (i, a) in formats.iter().enumerate() {
for (j, b) in formats.iter().enumerate() {
if i != j {
assert_ne!(a, b, "Expected {:?} != {:?}", a, b);
}
}
}
}
#[test]
fn test_detect_format_trailing_dot() {
let path = Path::new("/models/model.");
assert_eq!(detect_format(path), ModelFormat::Demo);
}
#[test]
fn test_detect_format_multiple_dots_gguf() {
let path = Path::new("/models/model.v1.2.3.gguf");
assert_eq!(detect_format(path), ModelFormat::Gguf);
}
#[test]
fn test_detect_format_hash_named_apr() {
let path = Path::new("/models/e910cab26ae116eb.apr");
assert_eq!(detect_format(path), ModelFormat::Apr);
}
#[test]
fn test_detect_format_hash_named_gguf() {
let path = Path::new("/cache/d4c4d9763127153c.gguf");
assert_eq!(detect_format(path), ModelFormat::Gguf);
}
#[test]
fn test_detect_format_long_extension() {
let path = Path::new("/models/model.safetensorsbackup");
assert_eq!(detect_format(path), ModelFormat::Demo);
}
#[test]
fn test_detect_format_similar_extensions() {
assert_eq!(detect_format(Path::new("x.ap")), ModelFormat::Demo);
assert_eq!(detect_format(Path::new("x.ggu")), ModelFormat::Demo);
assert_eq!(detect_format(Path::new("x.safetensor")), ModelFormat::Demo);
assert_eq!(detect_format(Path::new("x.ggufx")), ModelFormat::Demo);
assert_eq!(detect_format(Path::new("x.aprx")), ModelFormat::Demo);
}
#[test]
fn test_print_welcome_banner_zero_temp_and_top_p() {
let path = Path::new("/models/test.gguf");
let config = ChatConfig {
temperature: 0.0,
top_p: 0.0,
max_tokens: 1,
..Default::default()
};
print_welcome_banner(path, &config);
}
#[test]
fn test_print_welcome_banner_high_temp_and_top_p() {
let path = Path::new("/models/test.apr");
let config = ChatConfig {
temperature: 2.0,
top_p: 1.0,
max_tokens: 8192,
system: Some("Be creative and wild!".to_string()),
inspect: true,
..Default::default()
};
print_welcome_banner(path, &config);
}
#[test]
fn test_print_welcome_banner_openhermes_model() {
let path = Path::new("/models/openhermes-2.5.gguf");
let config = ChatConfig::default();
print_welcome_banner(path, &config);
}
#[test]
fn test_print_welcome_banner_yi_model() {
let path = Path::new("/models/yi-34b.gguf");
let config = ChatConfig::default();
print_welcome_banner(path, &config);
}
#[test]
fn test_print_welcome_banner_vicuna_model() {
let path = Path::new("/models/vicuna-7b.gguf");
let config = ChatConfig::default();
print_welcome_banner(path, &config);
}
#[test]
fn test_print_welcome_banner_mixtral_model() {
let path = Path::new("/models/mixtral-8x7b.gguf");
let config = ChatConfig::default();
print_welcome_banner(path, &config);
}