use super::*;
use crate::chunk::ChunkInput;
use crate::error::UrnaError;
use crate::layout::{
SECTION_EMBEDDINGS, SECTION_ENCODING_FLOAT16, SECTION_ENCODING_INT8, SECTION_ENCODING_RAW,
};
use crate::manifest::Manifest;
use crate::reader::UrnaView;
fn good_manifest() -> Manifest {
Manifest {
embedding_model: "demo".into(),
embedding_dim: 4,
n_chunks: 1,
chunker_version: "demo-chunker/1".into(),
model_hash: format!("sha256:{}", "0".repeat(64)),
..Default::default()
}
}
fn one_chunk() -> ChunkInput {
ChunkInput {
canonical_text: "hello world".into(),
source_uri: "doc.txt".into(),
byte_start: 0,
byte_end: 11,
embedding: vec![1.0, 0.0, 0.0, 0.0],
}
}
#[test]
fn deterministic_build() {
let b1 = UrnaFileBuilder::new(good_manifest())
.add_chunk(one_chunk())
.build_bytes()
.unwrap();
let b2 = UrnaFileBuilder::new(good_manifest())
.add_chunk(one_chunk())
.build_bytes()
.unwrap();
assert_eq!(b1, b2);
}
#[test]
fn reproducible_mode_overrides_created() {
let mut m1 = good_manifest();
m1.created = Some("2026-04-27T13:30:00Z".into());
let mut m2 = good_manifest();
m2.created = Some("2030-12-31T23:59:59Z".into());
let b1 = UrnaFileBuilder::new(m1)
.reproducible(true)
.add_chunk(one_chunk())
.build_bytes()
.unwrap();
let b2 = UrnaFileBuilder::new(m2)
.reproducible(true)
.add_chunk(one_chunk())
.build_bytes()
.unwrap();
assert_eq!(b1, b2, "reproducible builds must be byte-equal");
let view = UrnaView::from_bytes(&b1).unwrap();
assert_eq!(view.manifest.created.as_deref(), Some(REPRODUCIBLE_CREATED));
}
#[test]
fn non_reproducible_preserves_created() {
let mut m = good_manifest();
m.created = Some("2026-04-27T13:30:00Z".into());
let bytes = UrnaFileBuilder::new(m.clone())
.add_chunk(one_chunk())
.build_bytes()
.unwrap();
let view = UrnaView::from_bytes(&bytes).unwrap();
assert_eq!(
view.manifest.created.as_deref(),
Some("2026-04-27T13:30:00Z")
);
}
#[test]
fn rejects_chunk_count_mismatch() {
let mut m = good_manifest();
m.n_chunks = 2;
let err = UrnaFileBuilder::new(m).add_chunk(one_chunk()).build_bytes();
assert!(matches!(err, Err(UrnaError::ManifestInvalid(_))));
}
#[test]
fn rejects_bad_dim() {
let mut c = one_chunk();
c.embedding = vec![1.0, 0.0]; let err = UrnaFileBuilder::new(good_manifest())
.add_chunk(c)
.build_bytes();
assert!(matches!(err, Err(UrnaError::DimensionMismatch { .. })));
}
#[test]
#[cfg_attr(miri, ignore)] fn zstd_encoding_preserves_content_hash() {
let mut m = good_manifest();
m.n_chunks = 3;
let chunks: Vec<ChunkInput> = (0..3)
.map(|i| ChunkInput {
canonical_text: format!("paragraph number {}", i),
source_uri: "doc.txt".into(),
byte_start: i * 10,
byte_end: i * 10 + 5,
embedding: {
let mut v = vec![0.0; 4];
v[(i % 4) as usize] = 1.0;
v
},
})
.collect();
let raw = UrnaFileBuilder::new(m.clone())
.add_chunks(chunks.clone())
.build_bytes()
.unwrap();
let zst = UrnaFileBuilder::new(m)
.text_encoding(SectionEncoding::Zstd)
.add_chunks(chunks)
.build_bytes()
.unwrap();
let v_raw = UrnaView::from_bytes(&raw).unwrap();
let v_zst = UrnaView::from_bytes(&zst).unwrap();
assert_eq!(
v_raw.content_hash_hex().unwrap(),
v_zst.content_hash_hex().unwrap(),
"content_hash must equal across wire encodings"
);
assert_ne!(
v_raw.file_hash_hex(),
v_zst.file_hash_hex(),
"file_hash must differ between raw and zstd"
);
}
#[test]
#[cfg_attr(all(miri, target_arch = "aarch64"), ignore)] fn float16_embeddings_roundtrip() {
let mut m = good_manifest();
m.n_chunks = 2;
let bytes = UrnaFileBuilder::new(m)
.embedding_dtype(EmbeddingDType::Float16)
.add_chunk(ChunkInput {
canonical_text: "a".into(),
source_uri: "d".into(),
byte_start: 0,
byte_end: 1,
embedding: vec![1.0, 0.0, 0.0, 0.0],
})
.add_chunk(ChunkInput {
canonical_text: "b".into(),
source_uri: "d".into(),
byte_start: 1,
byte_end: 2,
embedding: vec![0.0, 1.0, 0.0, 0.0],
})
.build_bytes()
.unwrap();
let view = UrnaView::from_bytes(&bytes).unwrap();
assert_eq!(view.manifest.dtype, "float16");
let entry = view.entry(SECTION_EMBEDDINGS).unwrap();
assert_eq!(entry.encoding, SECTION_ENCODING_FLOAT16);
assert_eq!(entry.size as usize, 2 * 4 * 2); }
#[test]
fn int8_embeddings_roundtrip() {
let mut m = good_manifest();
m.n_chunks = 2;
let bytes = UrnaFileBuilder::new(m)
.embedding_dtype(EmbeddingDType::Int8)
.add_chunk(ChunkInput {
canonical_text: "a".into(),
source_uri: "d".into(),
byte_start: 0,
byte_end: 1,
embedding: vec![1.0, 0.0, 0.0, 0.0],
})
.add_chunk(ChunkInput {
canonical_text: "b".into(),
source_uri: "d".into(),
byte_start: 1,
byte_end: 2,
embedding: vec![0.0, 1.0, 0.0, 0.0],
})
.build_bytes()
.unwrap();
let view = UrnaView::from_bytes(&bytes).unwrap();
assert_eq!(view.manifest.dtype, "int8");
let entry = view.entry(SECTION_EMBEDDINGS).unwrap();
assert_eq!(entry.encoding, SECTION_ENCODING_INT8);
assert_eq!(entry.size as usize, 8 + 2 * 4 + 2 * 4);
}
#[test]
#[cfg_attr(miri, ignore)] fn two_compressed_builds_byte_identical_with_text_levers() {
let mut m = good_manifest();
let uniques: Vec<String> = (0..30)
.map(|i| {
(0..300)
.map(|j| char::from(b'a' + (((i * 13 + j * 5) % 26) as u8)))
.collect::<String>()
})
.collect();
let mut texts: Vec<String> = Vec::new();
for _ in 0..12 {
for u in &uniques {
texts.push(u.clone());
}
}
m.n_chunks = texts.len() as u64;
let chunks: Vec<ChunkInput> = texts
.iter()
.enumerate()
.map(|(i, t)| ChunkInput {
canonical_text: t.clone(),
source_uri: format!("doc://{}", i % 5),
byte_start: 0,
byte_end: t.len() as u64,
embedding: {
let mut v = vec![0.0f32; 4];
v[i % 4] = 1.0;
v
},
})
.collect();
let b1 = UrnaFileBuilder::new(m.clone())
.text_encoding(SectionEncoding::Zstd)
.reproducible(true)
.add_chunks(chunks.clone())
.build_bytes()
.unwrap();
let b2 = UrnaFileBuilder::new(m)
.text_encoding(SectionEncoding::Zstd)
.reproducible(true)
.add_chunks(chunks)
.build_bytes()
.unwrap();
assert_eq!(b1, b2, "two compressed builds must be byte-identical");
let view = UrnaView::from_bytes(&b1).unwrap();
view.content_hash_hex().unwrap();
}
#[test]
#[cfg_attr(miri, ignore)] fn rejects_zstd_embeddings() {
let m = good_manifest();
let bytes = UrnaFileBuilder::new(m)
.text_encoding(SectionEncoding::Zstd)
.add_chunk(one_chunk())
.build_bytes()
.unwrap();
let view = UrnaView::from_bytes(&bytes).unwrap();
let entry = view.entry(SECTION_EMBEDDINGS).unwrap();
assert_eq!(entry.encoding, SECTION_ENCODING_RAW);
}