use std::io::{Read, Write};
use tensor_wasm_artifacts::{
ArtifactError, ArtifactStore, DiskArtifactStore, InMemoryArtifactStore, ARTIFACT_HEADER_LEN,
ARTIFACT_HMAC_LEN,
};
fn sole_blob_file(dir: &std::path::Path) -> std::path::PathBuf {
let mut hits = Vec::new();
for entry in std::fs::read_dir(dir).expect("read_dir").flatten() {
let name = entry.file_name().into_string().unwrap_or_default();
if name.ends_with(".bin") {
hits.push(entry.path());
}
}
assert_eq!(hits.len(), 1, "expected one blob, found {hits:?}");
hits.into_iter().next().unwrap()
}
#[test]
fn disk_get_to_round_trip() {
let tmp = tempfile::tempdir().expect("tempdir");
let store = DiskArtifactStore::new(tmp.path().to_path_buf(), [0x12; 32]);
let payload: Vec<u8> = (0..200_000u32).map(|n| (n % 251) as u8).collect();
let hash = store.put(&payload).expect("put");
let mut sink: Vec<u8> = Vec::new();
let written = store.get_to(&hash, &mut sink).expect("get_to");
assert_eq!(written, payload.len() as u64, "byte count matches");
assert_eq!(sink, payload, "streamed bytes match the payload");
assert_eq!(store.get(&hash).expect("get"), sink);
}
#[test]
fn in_memory_get_to_round_trip() {
let store = InMemoryArtifactStore::new([0x34; 32]);
let payload = b"in-memory streaming payload".to_vec();
let hash = store.put(&payload).expect("put");
let mut sink: Vec<u8> = Vec::new();
let written = store.get_to(&hash, &mut sink).expect("get_to");
assert_eq!(written, payload.len() as u64);
assert_eq!(sink, payload);
}
#[test]
fn disk_get_to_missing_is_not_found() {
let tmp = tempfile::tempdir().expect("tempdir");
let store = DiskArtifactStore::new(tmp.path().to_path_buf(), [0x56; 32]);
let hash = tensor_wasm_artifacts::ContentHash::from_bytes([0x99; 32]);
let mut sink: Vec<u8> = Vec::new();
let err = store.get_to(&hash, &mut sink).expect_err("must miss");
assert!(matches!(err, ArtifactError::NotFound(_)), "got {err:?}");
assert!(sink.is_empty(), "no bytes written on a miss");
}
struct RecordingWriter {
bytes: Vec<u8>,
}
impl Write for RecordingWriter {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.bytes.extend_from_slice(buf);
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
#[test]
fn disk_get_to_tamper_emits_no_unverified_bytes() {
let tmp = tempfile::tempdir().expect("tempdir");
let store = DiskArtifactStore::new(tmp.path().to_path_buf(), [0x7A; 32]);
let payload = b"the quick brown fox jumps over the lazy dog, repeated enough \
times that zstd has a real body to chew through and the HMAC \
covers more than one stream buffer of compressed output here";
let hash = store.put(payload).expect("put");
let mut clean = RecordingWriter { bytes: Vec::new() };
store.get_to(&hash, &mut clean).expect("clean get_to");
assert_eq!(clean.bytes, payload);
let path = sole_blob_file(tmp.path());
let mut bytes = Vec::new();
std::fs::File::open(&path)
.expect("open")
.read_to_end(&mut bytes)
.expect("read");
assert!(bytes.len() > ARTIFACT_HEADER_LEN + ARTIFACT_HMAC_LEN);
bytes[ARTIFACT_HEADER_LEN + 2] ^= 0xFF;
std::fs::write(&path, &bytes).expect("write tampered");
let mut sink = RecordingWriter { bytes: Vec::new() };
let err = store
.get_to(&hash, &mut sink)
.expect_err("must reject tamper");
assert!(matches!(err, ArtifactError::BadHmac), "got {err:?}");
assert!(
sink.bytes.is_empty(),
"no unverified bytes may reach the writer on tamper; got {} bytes",
sink.bytes.len()
);
}
#[test]
fn disk_get_to_tampered_tag_rejected_with_empty_sink() {
let tmp = tempfile::tempdir().expect("tempdir");
let store = DiskArtifactStore::new(tmp.path().to_path_buf(), [0x9C; 32]);
let hash = store.put(b"sign me").expect("put");
let path = sole_blob_file(tmp.path());
let mut bytes = std::fs::read(&path).expect("read");
let last = bytes.len() - 1;
bytes[last] ^= 0x01; std::fs::write(&path, &bytes).expect("write");
let mut sink = RecordingWriter { bytes: Vec::new() };
let err = store.get_to(&hash, &mut sink).expect_err("must reject");
assert!(matches!(err, ArtifactError::BadHmac), "got {err:?}");
assert!(sink.bytes.is_empty(), "no bytes on tampered-tag reject");
}