use std::fs;
use std::io;
use std::path::Path;
use iroh_blobs::format::collection::Collection;
use radicle_artifact_core::cid::{
blake3_hash_to_cid, canonical_walk, compute_content_id, ArtifactKind, Cid,
};
fn compute_content_id_iroh_blobs(dir: &Path) -> Result<Cid, io::Error> {
let entries: Vec<(String, iroh_blobs::Hash)> = canonical_walk(dir)?
.into_iter()
.map(|(name, path)| {
let bytes = fs::read(&path)?;
Ok((name, iroh_blobs::Hash::new(&bytes)))
})
.collect::<Result<_, io::Error>>()?;
let collection = Collection::from_iter(entries);
let root_blob = collection.to_blobs().last().ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidData, "collection produced no blobs")
})?;
Ok(blake3_hash_to_cid(
blake3::hash(&root_blob),
ArtifactKind::Collection,
))
}
#[test]
fn content_id_matches_iroh_blobs_collection_encoding() {
let dir = tempfile::TempDir::new().unwrap();
let files: &[(&str, &[u8])] = &[
("hello.txt", b"hello"),
("sub/world.txt", b"world"),
("file with spaces.txt", b"spaces matter"),
(".hidden", b"hidden file"),
("empty.txt", b""),
("unicode-\u{30c6}\u{30b9}\u{30c8}.bin", b"\x00\x01\xff"),
("deep/nested/path/file.txt", b"deeply nested"),
];
for (path, contents) in files {
let file_path = dir.path().join(path);
fs::create_dir_all(file_path.parent().unwrap()).unwrap();
fs::write(&file_path, contents).unwrap();
}
let ours = compute_content_id(dir.path()).unwrap();
let theirs = compute_content_id_iroh_blobs(dir.path()).unwrap();
assert_eq!(
ours, theirs,
"core's CollectionV0/HashSeq reimplementation drifted from iroh-blobs"
);
}
#[test]
fn empty_dir_matches() {
let dir = tempfile::TempDir::new().unwrap();
let ours = compute_content_id(dir.path()).unwrap();
let theirs = compute_content_id_iroh_blobs(dir.path()).unwrap();
assert_eq!(ours, theirs);
}