use blazehash::algorithm::Algorithm;
use blazehash::format::{write_csv, write_dfxml, write_json, write_jsonl, write_sumfile};
use blazehash::hash::FileHashResult;
use std::collections::HashMap;
use std::path::PathBuf;
fn sample_result() -> FileHashResult {
let mut hashes = HashMap::new();
hashes.insert(
Algorithm::Blake3,
"d74981efa70a0c880b8d8c1985d075dbcbf679b99a5f9914e5aaf96b831a9e24".to_string(),
);
hashes.insert(
Algorithm::Sha256,
"b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9".to_string(),
);
FileHashResult {
path: PathBuf::from("/evidence/test.txt"),
size: 11,
hashes,
}
}
#[test]
fn csv_output_has_headers() {
let results = vec![sample_result()];
let algos = vec![Algorithm::Blake3, Algorithm::Sha256];
let mut buf = Vec::new();
write_csv(&mut buf, &results, &algos).unwrap();
let output = String::from_utf8(buf).unwrap();
assert!(output.starts_with("size,blake3,sha256,filename\n"));
}
#[test]
fn csv_output_has_data() {
let results = vec![sample_result()];
let algos = vec![Algorithm::Blake3, Algorithm::Sha256];
let mut buf = Vec::new();
write_csv(&mut buf, &results, &algos).unwrap();
let output = String::from_utf8(buf).unwrap();
let lines: Vec<&str> = output.lines().collect();
assert_eq!(lines.len(), 2);
assert!(lines[1].starts_with("11,"));
assert!(lines[1].ends_with("/evidence/test.txt"));
}
#[test]
fn json_output_is_valid() {
let results = vec![sample_result()];
let algos = vec![Algorithm::Blake3];
let mut buf = Vec::new();
write_json(&mut buf, &results, &algos).unwrap();
let output = String::from_utf8(buf).unwrap();
let parsed: serde_json::Value = serde_json::from_str(&output).unwrap();
assert!(parsed.is_array());
assert_eq!(parsed.as_array().unwrap().len(), 1);
}
#[test]
fn jsonl_output_one_per_line() {
let results = vec![sample_result(), sample_result()];
let algos = vec![Algorithm::Blake3];
let mut buf = Vec::new();
write_jsonl(&mut buf, &results, &algos).unwrap();
let output = String::from_utf8(buf).unwrap();
let lines: Vec<&str> = output.lines().collect();
assert_eq!(lines.len(), 2);
for line in &lines {
let _: serde_json::Value = serde_json::from_str(line).unwrap();
}
}
#[test]
fn csv_missing_algorithm_returns_error() {
let mut hashes = HashMap::new();
hashes.insert(Algorithm::Blake3, "abcd1234".to_string());
let result = FileHashResult {
path: PathBuf::from("/test.txt"),
size: 11,
hashes,
};
let mut buf = Vec::new();
let err =
blazehash::format::write_csv(&mut buf, &[result], &[Algorithm::Blake3, Algorithm::Sha256]);
assert!(err.is_err(), "should error when algorithm hash is missing");
}
#[test]
fn csv_empty_results() {
let mut buf = Vec::new();
write_csv(&mut buf, &[], &[Algorithm::Blake3]).unwrap();
let output = String::from_utf8(buf).unwrap();
assert_eq!(output.lines().count(), 1);
assert!(output.starts_with("size,blake3,filename"));
}
#[test]
fn csv_multiple_results() {
let r1 = sample_result();
let mut hashes2 = HashMap::new();
hashes2.insert(Algorithm::Blake3, "aaaa".to_string());
hashes2.insert(Algorithm::Sha256, "bbbb".to_string());
let r2 = FileHashResult {
path: PathBuf::from("/evidence/other.txt"),
size: 42,
hashes: hashes2,
};
let algos = vec![Algorithm::Blake3, Algorithm::Sha256];
let mut buf = Vec::new();
write_csv(&mut buf, &[r1, r2], &algos).unwrap();
let output = String::from_utf8(buf).unwrap();
assert_eq!(output.lines().count(), 3); }
#[test]
fn json_empty_results() {
let mut buf = Vec::new();
write_json(&mut buf, &[], &[Algorithm::Blake3]).unwrap();
let output = String::from_utf8(buf).unwrap();
let parsed: serde_json::Value = serde_json::from_str(&output).unwrap();
assert_eq!(parsed.as_array().unwrap().len(), 0);
}
#[test]
fn json_structure_has_expected_fields() {
let results = vec![sample_result()];
let algos = vec![Algorithm::Blake3, Algorithm::Sha256];
let mut buf = Vec::new();
write_json(&mut buf, &results, &algos).unwrap();
let output = String::from_utf8(buf).unwrap();
let parsed: serde_json::Value = serde_json::from_str(&output).unwrap();
let entry = &parsed[0];
assert!(entry.get("filename").is_some());
assert!(entry.get("size").is_some());
assert!(entry.get("hashes").is_some());
assert_eq!(entry["size"], 11);
assert!(entry["hashes"].get("blake3").is_some());
assert!(entry["hashes"].get("sha256").is_some());
}
#[test]
fn jsonl_empty_results() {
let mut buf = Vec::new();
write_jsonl(&mut buf, &[], &[Algorithm::Blake3]).unwrap();
let output = String::from_utf8(buf).unwrap();
assert!(output.is_empty());
}
#[test]
fn dfxml_output_has_root_element() {
let results = vec![sample_result()];
let algos = vec![Algorithm::Blake3, Algorithm::Sha256];
let mut buf = Vec::new();
write_dfxml(&mut buf, &results, &algos).unwrap();
let output = String::from_utf8(buf).unwrap();
assert!(output.contains("<dfxml"));
assert!(output.contains("</dfxml>"));
assert!(output.contains("<fileobject>"));
assert!(output.contains("<filesize>11</filesize>"));
}
#[test]
fn dfxml_output_has_hash_digests() {
let results = vec![sample_result()];
let algos = vec![Algorithm::Blake3];
let mut buf = Vec::new();
write_dfxml(&mut buf, &results, &algos).unwrap();
let output = String::from_utf8(buf).unwrap();
assert!(
output.contains("hashdigest type='BLAKE3'")
|| output.contains("hashdigest type='blake3'")
|| output.contains("hashdigest")
);
}
#[test]
fn dfxml_empty_results() {
let mut buf = Vec::new();
write_dfxml(&mut buf, &[], &[Algorithm::Blake3]).unwrap();
let output = String::from_utf8(buf).unwrap();
assert!(output.contains("<dfxml"));
assert!(!output.contains("<fileobject>"));
}
#[test]
fn sumfile_output_hash_two_spaces_path() {
let mut hashes = std::collections::HashMap::new();
hashes.insert(
Algorithm::Sha256,
"abc123def456abc123def456abc123def456abc123def456abc123def456abc1".to_string(),
);
let result = FileHashResult {
path: std::path::PathBuf::from("/evidence/test.bin"),
size: 42,
hashes,
};
let mut buf = Vec::new();
write_sumfile(&mut buf, &[result], &[Algorithm::Sha256]).unwrap();
let output = String::from_utf8(buf).unwrap();
assert!(
output.contains(" /evidence/test.bin"),
"expected two-space separator"
);
}
#[test]
fn sumfile_error_on_multiple_algorithms() {
let mut buf = Vec::new();
let result = sample_result();
let err = write_sumfile(&mut buf, &[result], &[Algorithm::Blake3, Algorithm::Sha256]);
assert!(
err.is_err(),
"sumfile should error with multiple algorithms"
);
}
#[test]
fn json_missing_algorithm_silently_skipped() {
let mut hashes = HashMap::new();
hashes.insert(Algorithm::Blake3, "abcd1234".to_string());
let result = FileHashResult {
path: PathBuf::from("/test.txt"),
size: 11,
hashes,
};
let mut buf = Vec::new();
write_json(&mut buf, &[result], &[Algorithm::Blake3, Algorithm::Sha256]).unwrap();
let output = String::from_utf8(buf).unwrap();
let parsed: serde_json::Value = serde_json::from_str(&output).unwrap();
assert!(parsed[0]["hashes"].get("blake3").is_some());
assert!(parsed[0]["hashes"].get("sha256").is_none());
}