use super::*;
use std::collections::HashMap;
use std::io::Write;
use tempfile::{tempdir, NamedTempFile};
#[test]
fn test_validate_path_not_found() {
let result = validate_path(Path::new("/nonexistent/model.apr"));
assert!(result.is_err());
match result {
Err(CliError::FileNotFound(_)) => {}
_ => panic!("Expected FileNotFound error"),
}
}
#[test]
fn test_validate_path_is_directory() {
let dir = tempdir().expect("create temp dir");
let result = validate_path(dir.path());
assert!(result.is_err());
match result {
Err(CliError::NotAFile(_)) => {}
_ => panic!("Expected NotAFile error"),
}
}
#[test]
fn test_validate_path_valid_file() {
let file = NamedTempFile::new().expect("create temp file");
let result = validate_path(file.path());
assert!(result.is_ok());
}
#[test]
fn test_run_file_not_found() {
let result = run(
Path::new("/nonexistent/model.apr"),
false,
false,
None,
false,
false,
);
assert!(result.is_err());
match result {
Err(CliError::FileNotFound(_)) => {}
_ => panic!("Expected FileNotFound error"),
}
}
#[test]
fn test_run_is_directory() {
let dir = tempdir().expect("create temp dir");
let result = run(dir.path(), false, false, None, false, false);
assert!(result.is_err());
match result {
Err(CliError::NotAFile(_)) => {}
_ => panic!("Expected NotAFile error"),
}
}
#[test]
fn test_run_invalid_file() {
let mut file = NamedTempFile::with_suffix(".apr").expect("create temp file");
file.write_all(b"not a valid APR file").expect("write");
let result = run(file.path(), false, false, None, false, false);
assert!(result.is_err());
}
#[test]
fn test_run_with_quality_flag() {
let mut file = NamedTempFile::with_suffix(".apr").expect("create temp file");
file.write_all(b"invalid data").expect("write");
let result = run(file.path(), true, false, None, false, false);
assert!(result.is_err());
}
#[test]
fn test_run_with_min_score() {
let mut file = NamedTempFile::with_suffix(".apr").expect("create temp file");
file.write_all(b"invalid data").expect("write");
let result = run(file.path(), false, false, Some(100), false, false);
assert!(result.is_err());
}
#[test]
fn test_run_with_strict_flag() {
let mut file = NamedTempFile::with_suffix(".apr").expect("create temp file");
file.write_all(b"test data").expect("write");
let result = run(file.path(), false, true, None, false, false);
assert!(result.is_err());
}
#[test]
fn test_run_with_all_flags() {
let mut file = NamedTempFile::with_suffix(".apr").expect("create temp file");
file.write_all(b"test data").expect("write");
let result = run(file.path(), true, true, Some(50), false, false);
assert!(result.is_err());
}
#[test]
fn test_run_empty_file() {
let file = NamedTempFile::with_suffix(".apr").expect("create temp file");
let result = run(file.path(), false, false, None, false, false);
assert!(result.is_err());
}
#[test]
fn test_quality_assessment_display() {
let mut category_scores = HashMap::new();
category_scores.insert(Category::Structure, 25);
category_scores.insert(Category::Physics, 20);
category_scores.insert(Category::Tooling, 15);
category_scores.insert(Category::Conversion, 10);
let report = ValidationReport {
checks: Vec::new(),
total_score: 70,
category_scores,
};
print_quality_assessment(&report);
}
#[test]
fn test_quality_assessment_missing_categories() {
let report = ValidationReport {
checks: Vec::new(),
total_score: 0,
category_scores: HashMap::new(),
};
print_quality_assessment(&report);
}
#[test]
fn test_quality_assessment_all_score_ranges() {
let mut high_scores = HashMap::new();
high_scores.insert(Category::Structure, 25);
high_scores.insert(Category::Physics, 25);
high_scores.insert(Category::Tooling, 25);
high_scores.insert(Category::Conversion, 25);
let high_report = ValidationReport {
checks: Vec::new(),
total_score: 100,
category_scores: high_scores,
};
let mut low_scores = HashMap::new();
low_scores.insert(Category::Structure, 5);
let low_report = ValidationReport {
checks: Vec::new(),
total_score: 5,
category_scores: low_scores,
};
print_quality_assessment(&high_report);
print_quality_assessment(&low_report);
}
#[test]
fn test_print_summary_valid_report() {
let report = ValidationReport {
checks: Vec::new(), total_score: 100,
category_scores: HashMap::new(),
};
let result = print_summary(&report);
assert!(result.is_ok());
}
#[test]
fn test_print_quality_assessment_empty() {
let report = ValidationReport {
checks: Vec::new(),
total_score: 0,
category_scores: HashMap::new(),
};
print_quality_assessment(&report);
}
#[test]
fn test_run_gguf_format_dispatch() {
use aprender::format::gguf::{export_tensors_to_gguf, GgmlType, GgufTensor, GgufValue};
let floats: Vec<f32> = (0..16).map(|i| (i as f32 + 1.0) * 0.1).collect();
let data: Vec<u8> = floats.iter().flat_map(|f| f.to_le_bytes()).collect();
let tensor = GgufTensor {
name: "model.weight".to_string(),
shape: vec![4, 4],
dtype: GgmlType::F32,
data,
};
let metadata = vec![(
"general.architecture".to_string(),
GgufValue::String("test".to_string()),
)];
let mut gguf_bytes = Vec::new();
export_tensors_to_gguf(&mut gguf_bytes, &[tensor], &metadata).expect("export GGUF");
let mut file = NamedTempFile::with_suffix(".gguf").expect("create temp file");
file.write_all(&gguf_bytes).expect("write GGUF");
let result = run(file.path(), false, false, None, false, false);
assert!(result.is_ok(), "GGUF format dispatch should work");
}
#[test]
fn test_run_safetensors_format_dispatch() {
let header_json = serde_json::json!({
"test.weight": {
"dtype": "F32",
"shape": [2, 2],
"data_offsets": [0, 16]
}
});
let header_bytes = serde_json::to_vec(&header_json).expect("serialize header");
let header_len = header_bytes.len() as u64;
let mut st_bytes = Vec::new();
st_bytes.extend_from_slice(&header_len.to_le_bytes());
st_bytes.extend_from_slice(&header_bytes);
let floats: [f32; 4] = [1.0, 2.0, 3.0, 4.0];
for f in floats {
st_bytes.extend_from_slice(&f.to_le_bytes());
}
let mut file = NamedTempFile::with_suffix(".safetensors").expect("create temp file");
file.write_all(&st_bytes).expect("write SafeTensors");
let result = run(file.path(), false, false, None, false, false);
assert!(result.is_ok(), "SafeTensors format dispatch should work");
}
#[test]
fn test_run_gguf_format_detection_by_magic() {
use aprender::format::gguf::{export_tensors_to_gguf, GgmlType, GgufTensor, GgufValue};
let floats: [f32; 4] = [1.0, 2.0, 3.0, 4.0];
let tensor_data: Vec<u8> = floats.iter().flat_map(|f| f.to_le_bytes()).collect();
let tensor = GgufTensor {
name: "test.weight".to_string(),
shape: vec![2, 2],
dtype: GgmlType::F32,
data: tensor_data,
};
let metadata = vec![(
"general.architecture".to_string(),
GgufValue::String("test".to_string()),
)];
let mut gguf_bytes = Vec::new();
export_tensors_to_gguf(&mut gguf_bytes, &[tensor], &metadata).expect("export GGUF");
let mut file = NamedTempFile::with_suffix(".bin").expect("create temp file");
file.write_all(&gguf_bytes).expect("write GGUF");
let result = run(file.path(), false, false, None, false, false);
assert!(result.is_ok(), "Should detect GGUF by magic bytes");
}
#[test]
fn test_run_gguf_with_physics_violations() {
use aprender::format::gguf::{export_tensors_to_gguf, GgmlType, GgufTensor, GgufValue};
let nan_f32 = f32::NAN.to_le_bytes();
let mut tensor_data = Vec::new();
for _ in 0..4 {
tensor_data.extend_from_slice(&nan_f32);
}
let tensor = GgufTensor {
name: "model.weight".to_string(),
shape: vec![2, 2],
dtype: GgmlType::F32,
data: tensor_data,
};
let metadata = vec![(
"general.architecture".to_string(),
GgufValue::String("test".to_string()),
)];
let mut gguf_bytes = Vec::new();
export_tensors_to_gguf(&mut gguf_bytes, &[tensor], &metadata).expect("export GGUF");
let mut file = NamedTempFile::with_suffix(".gguf").expect("create temp file");
file.write_all(&gguf_bytes).expect("write GGUF");
let result = run(file.path(), false, false, None, false, false);
assert!(result.is_err(), "Should fail with NaN tensors");
}
fn write_apr_fixture(tensors: &[(&str, Vec<usize>, Vec<f32>)]) -> NamedTempFile {
use aprender::format::v2::{AprV2Metadata, AprV2Writer};
let metadata = AprV2Metadata::new("test");
let mut writer = AprV2Writer::new(metadata);
for (name, shape, data) in tensors {
writer.add_tensor_f32_owned(*name, shape.clone(), data.clone());
}
let mut apr_bytes = Vec::new();
writer.write_to(&mut apr_bytes).expect("write APR fixture");
let mut file = NamedTempFile::with_suffix(".apr").expect("create temp file");
file.write_all(&apr_bytes).expect("write apr bytes");
file
}
fn healthy_weights(n: usize) -> Vec<f32> {
(0..n).map(|i| ((i as f32) * 0.013 - 0.5) * 0.2).collect()
}
#[test]
fn pmat926_falsifier_all_zero_lm_head_apr_rejected() {
let file = write_apr_fixture(&[
("lm_head.weight", vec![8, 4], vec![0.0; 32]),
("model.embed_tokens.weight", vec![4, 4], healthy_weights(16)),
]);
let result = run(file.path(), false, false, None, false, false);
assert!(
result.is_err(),
"all-zero lm_head .apr must be REJECTED (F-VALIDATE-APR-DISPATCH-001), got Ok"
);
}
#[test]
fn pmat926_falsifier_nan_tensor_apr_rejected() {
let mut nan_block = healthy_weights(32);
nan_block[5] = f32::NAN;
nan_block[17] = f32::NAN;
let file = write_apr_fixture(&[
("lm_head.weight", vec![8, 4], healthy_weights(32)),
("model.layers.0.mlp.down_proj.weight", vec![8, 4], nan_block),
]);
let result = run(file.path(), false, false, None, false, false);
assert!(
result.is_err(),
"NaN .apr must be REJECTED (F-VALIDATE-APR-DISPATCH-001), got Ok"
);
}
#[test]
fn pmat926_valid_apr_still_passes() {
let file = write_apr_fixture(&[
("lm_head.weight", vec![8, 4], healthy_weights(32)),
("model.embed_tokens.weight", vec![8, 4], healthy_weights(32)),
(
"model.layers.0.mlp.down_proj.weight",
vec![8, 4],
healthy_weights(32),
),
]);
let result = run(file.path(), false, false, None, false, false);
assert!(
result.is_ok(),
"healthy .apr must still PASS (no false positives), got {result:?}"
);
}
#[test]
fn pmat926_falsifier_strict_apr_all_zero_nonzero_exit() {
let file = write_apr_fixture(&[
("lm_head.weight", vec![8, 4], healthy_weights(32)),
(
"model.layers.0.self_attn.q_proj.bias",
vec![8],
vec![0.0; 8],
),
]);
let strict_result = run(file.path(), false, true, None, false, false);
assert!(
strict_result.is_err(),
"--strict on an all-zero-tensor .apr must exit non-zero (F-VALIDATE-STRICT-001), got Ok"
);
}
#[test]
fn pmat926_skip_contract_bypasses_content_gate() {
let file = write_apr_fixture(&[
("lm_head.weight", vec![8, 4], vec![0.0; 32]),
("model.embed_tokens.weight", vec![4, 4], healthy_weights(16)),
]);
let result = run(
file.path(),
false,
false,
None,
false,
true,
);
assert!(
result.is_ok(),
"--skip-contract must bypass the content gate, got {result:?}"
);
}
#[test]
fn pmat926_json_apr_all_zero_lm_head_rejected() {
let file = write_apr_fixture(&[
("lm_head.weight", vec![8, 4], vec![0.0; 32]),
("model.embed_tokens.weight", vec![4, 4], healthy_weights(16)),
]);
let result = run(file.path(), false, false, None, true, false);
assert!(
result.is_err(),
"JSON .apr path must fail closed on content-broken model, got Ok"
);
}