#[test]
fn qa_report_deserialize_ignores_unknown_fields() {
let json = r#"{
"model": "test.gguf",
"passed": true,
"gates": [],
"total_duration_ms": 100,
"timestamp": "2026-02-07T00:00:00Z",
"summary": "ok",
"extra_field": "should be ignored",
"another_extra": 42
}"#;
let report: QaReport = serde_json::from_str(json).expect("deserialize with extras");
assert_eq!(report.model, "test.gguf");
assert!(report.passed);
}
#[test]
fn gate_result_deserialize_ignores_unknown_fields() {
let json = r#"{
"name": "test",
"passed": true,
"message": "ok",
"duration_ms": 100,
"skipped": false,
"future_field": "v2"
}"#;
let result: GateResult = serde_json::from_str(json).expect("deserialize with extras");
assert_eq!(result.name, "test");
assert!(result.passed);
}
#[test]
fn verify_output_rejects_empty() {
let result = verify_output("", "test-001", &["4"]);
assert!(matches!(result, OutputVerification::Fail { .. }));
if let OutputVerification::Fail { reason } = result {
assert!(reason.contains("Empty"), "Expected 'Empty', got: {reason}");
}
}
#[test]
fn verify_output_rejects_whitespace_only() {
let result = verify_output(" \n\t ", "test-002", &["4"]);
assert!(matches!(result, OutputVerification::Fail { .. }));
}
#[test]
fn verify_output_rejects_garbage_fffd() {
let result = verify_output("The answer is \u{FFFD}\u{FFFD}", "test-003", &["4"]);
assert!(matches!(result, OutputVerification::Fail { .. }));
if let OutputVerification::Fail { reason } = result {
assert!(
reason.contains("Garbage"),
"Expected 'Garbage', got: {reason}"
);
}
}
#[test]
fn verify_output_rejects_garbage_unk() {
let result = verify_output("Hello [UNK] world", "test-004", &["Hello"]);
assert!(matches!(result, OutputVerification::Fail { .. }));
if let OutputVerification::Fail { reason } = result {
assert!(
reason.contains("Garbage"),
"Expected 'Garbage', got: {reason}"
);
}
}
#[test]
fn verify_output_rejects_qwen2_05b_observed_gibberish() {
let observed =
"ìłľê°Ģ udaÅĤo udaÅĤoá¿Ĩëĸ» Ãĥå udaÅĤo Ãĥ³³³³ zwiÄħzku";
let result = verify_output(observed, "FALSIFY-QA-GIBBERISH-001", &["Hello", "!"]);
assert!(
matches!(result, OutputVerification::Fail { .. }),
"Qwen2-0.5B observed gibberish must be rejected"
);
}
#[test]
fn verify_output_rejects_repeated_fragment() {
let result = verify_output(
"udaÅĤo udaÅĤo udaÅĤo udaÅĤo end",
"FALSIFY-QA-GIBBERISH-002",
&["end"],
);
assert!(
matches!(result, OutputVerification::Fail { .. }),
"4x-repeated fragment must trip the loop-pathology signal"
);
}
#[test]
fn verify_output_accepts_normal_english() {
let result = verify_output(
"The answer is 4. Hello world!",
"FALSIFY-QA-GIBBERISH-003",
&["4"],
);
assert!(
matches!(result, OutputVerification::Pass),
"Normal ASCII English must pass"
);
}
#[test]
fn verify_output_rejects_null_bytes() {
let result = verify_output("Hello\0World", "test-005", &["Hello"]);
assert!(matches!(result, OutputVerification::Fail { .. }));
if let OutputVerification::Fail { reason } = result {
assert!(
reason.contains("null"),
"Expected 'null bytes', got: {reason}"
);
}
}
#[test]
fn verify_output_rejects_missing_expected() {
let result = verify_output("The answer is five", "test-006", &["4"]);
assert!(matches!(result, OutputVerification::Fail { .. }));
if let OutputVerification::Fail { reason } = result {
assert!(
reason.contains("Expected"),
"Expected mention of pattern, got: {reason}"
);
}
}
#[test]
fn verify_output_accepts_correct() {
let result = verify_output("The answer is 4.", "test-007", &["4"]);
assert!(matches!(result, OutputVerification::Pass));
}
#[test]
fn verify_output_accepts_any_expected_pattern() {
let result = verify_output("Hi there!", "test-008", &["Hello", "Hi", "Hey"]);
assert!(matches!(result, OutputVerification::Pass));
}
#[test]
fn verify_output_case_insensitive() {
let result = verify_output("HELLO WORLD", "test-009", &["hello"]);
assert!(matches!(result, OutputVerification::Pass));
}
#[test]
fn verify_output_garbage_check_before_answer_check() {
let result = verify_output("4 [UNK] answer", "test-010", &["4"]);
assert!(matches!(result, OutputVerification::Fail { .. }));
if let OutputVerification::Fail { reason } = result {
assert!(
reason.contains("Garbage"),
"Garbage check must happen BEFORE answer check, got: {reason}"
);
}
}
#[test]
fn verify_output_no_expected_patterns_passes() {
let result = verify_output("Some valid output", "test-011", &[]);
assert!(matches!(result, OutputVerification::Pass));
}
#[test]
fn strip_thinking_no_tags() {
assert_eq!(strip_thinking_blocks("The answer is 4."), "The answer is 4.");
}
#[test]
fn strip_thinking_complete_block() {
let input = "<think>Let me calculate 2+2. That's 4.</think>4";
assert_eq!(strip_thinking_blocks(input), "4");
}
#[test]
fn strip_thinking_unclosed() {
let input = "<think>Let me think about this carefully...";
assert_eq!(strip_thinking_blocks(input), "");
}
#[test]
fn strip_thinking_multiline() {
let input = "<think>\nStep 1: 2+2\nStep 2: =4\n</think>\nThe answer is 4.";
assert_eq!(strip_thinking_blocks(input), "The answer is 4.");
}
#[test]
fn strip_thinking_multiple_blocks() {
let input = "<think>first thought</think>Hello <think>second thought</think>world";
assert_eq!(strip_thinking_blocks(input), "Hello world");
}
#[test]
fn strip_thinking_preserves_surrounding() {
let input = "Before <think>reasoning</think> After";
assert_eq!(strip_thinking_blocks(input), "Before After");
}
#[cfg(feature = "inference")]
#[test]
fn ollama_parity_grade_boundaries() {
assert_eq!(ollama_parity_grade(0.0), "F");
assert_eq!(ollama_parity_grade(0.3), "F");
assert_eq!(ollama_parity_grade(0.49), "F");
assert_eq!(ollama_parity_grade(0.5), "D");
assert_eq!(ollama_parity_grade(0.64), "D");
assert_eq!(ollama_parity_grade(0.74), "D");
assert_eq!(ollama_parity_grade(0.75), "C");
assert_eq!(ollama_parity_grade(0.99), "C");
assert_eq!(ollama_parity_grade(1.0), "B");
assert_eq!(ollama_parity_grade(1.49), "B");
assert_eq!(ollama_parity_grade(1.5), "A");
assert_eq!(ollama_parity_grade(1.99), "A");
assert_eq!(ollama_parity_grade(2.0), "A+");
assert_eq!(ollama_parity_grade(3.5), "A+");
}
#[test]
fn strip_quant_suffix_qwen3_q4k() {
assert_eq!(strip_quant_suffix("qwen3-8b-q4k"), "qwen3-8b");
}
#[test]
fn strip_quant_suffix_qwen3_q4_k_m() {
assert_eq!(strip_quant_suffix("qwen3-8b-q4_k_m"), "qwen3-8b");
}
#[test]
fn strip_quant_suffix_qwen3_q6k() {
assert_eq!(strip_quant_suffix("qwen3-8b-q6k"), "qwen3-8b");
}
#[test]
fn strip_quant_suffix_qwen3_f16() {
assert_eq!(strip_quant_suffix("qwen3-8b-f16"), "qwen3-8b");
}
#[test]
fn strip_quant_suffix_no_suffix() {
assert_eq!(strip_quant_suffix("qwen3-8b"), "qwen3-8b");
}
#[test]
fn strip_quant_suffix_qwen3_q8_0() {
assert_eq!(strip_quant_suffix("qwen3-8b-q8_0"), "qwen3-8b");
}
#[test]
fn discover_apr_cache_returns_none_for_nonexistent_model() {
let result = discover_apr_cache("nonexistent-model-xyzzy-9999");
assert!(result.is_none());
}
#[test]
fn find_sharded_safetensors_with_index_and_shard() {
let tmp = tempfile::tempdir().expect("create tempdir");
std::fs::write(
tmp.path().join("model.safetensors.index.json"),
r#"{"weight_map": {}}"#,
)
.expect("write index");
std::fs::write(
tmp.path().join("model-00001-of-00004.safetensors"),
b"fake shard",
)
.expect("write shard");
let shard = find_sharded_safetensors(tmp.path());
assert!(shard.is_some(), "Should find sharded safetensors");
let shard_path = shard.expect("shard exists");
assert!(
shard_path.to_string_lossy().ends_with(".safetensors"),
"Should find a .safetensors file"
);
}
#[test]
fn find_sharded_safetensors_returns_none_without_index() {
let tmp = tempfile::tempdir().expect("create tempdir");
std::fs::write(
tmp.path().join("model-00001-of-00002.safetensors"),
b"data",
)
.expect("write shard");
let result = find_sharded_safetensors(tmp.path());
assert!(
result.is_none(),
"Should return None without index.json present"
);
}
#[test]
fn discover_sibling_subdir_finds_single_model() {
let tmp = tempfile::tempdir().expect("create tempdir");
let subdir = tmp.path().join("qwen3-8b");
std::fs::create_dir_all(&subdir).expect("create subdir");
std::fs::write(subdir.join("model.safetensors"), b"fake model").expect("write model");
let result = discover_sibling_subdir(tmp.path(), "qwen3-8b");
assert!(result.is_some(), "Should find model.safetensors in subdir");
}
#[test]
fn discover_sibling_subdir_returns_none_for_missing_dir() {
let tmp = tempfile::tempdir().expect("create tempdir");
let result = discover_sibling_subdir(tmp.path(), "nonexistent");
assert!(result.is_none());
}