use rusqlite::Connection;
use tempfile::TempDir;
fn store_with_chunks(dir: &TempDir, n_chunks: usize, distinct_at: usize) -> std::path::PathBuf {
let db = dir.path().join("memory.db");
let conn = Connection::open(&db).expect("open");
conn.execute_batch(
"
CREATE TABLE documents (
id INTEGER PRIMARY KEY AUTOINCREMENT,
collection TEXT NOT NULL, path TEXT NOT NULL, title TEXT NOT NULL,
hash TEXT NOT NULL, created_at TEXT NOT NULL, modified_at TEXT NOT NULL,
active INTEGER NOT NULL DEFAULT 1, UNIQUE(collection, path));
CREATE TABLE content_vectors (
hash TEXT NOT NULL, seq INTEGER NOT NULL DEFAULT 0, pos INTEGER NOT NULL DEFAULT 0,
model TEXT NOT NULL, embedded_at TEXT NOT NULL, PRIMARY KEY (hash, seq));
CREATE TABLE vectors_vec (hash_seq TEXT PRIMARY KEY, embedding BLOB NOT NULL);
INSERT INTO documents (collection, path, title, hash, created_at, modified_at, active)
VALUES ('memory', 'doc.md', 'Doc', 'h1', 'now', 'now', 1);
",
)
.expect("schema");
for seq in 0..n_chunks {
let vec: Vec<f32> = if seq == distinct_at {
vec![1.0, 0.0, 0.0]
} else {
vec![0.0, 1.0, 0.0]
};
let blob: Vec<u8> = vec.iter().flat_map(|f| f.to_le_bytes()).collect();
conn.execute(
"INSERT INTO content_vectors (hash, seq, pos, model, embedded_at)
VALUES ('h1', ?1, ?2, 'test-model', 'now')",
rusqlite::params![seq as i64, (seq * 100) as i64],
)
.expect("insert cv");
conn.execute(
"INSERT INTO vectors_vec (hash_seq, embedding) VALUES (?1, ?2)",
rusqlite::params![format!("h1_{seq}"), blob],
)
.expect("insert vec");
}
db
}
#[test]
fn a_match_in_a_later_chunk_is_found() {
use crate::memory::vector_search::search_chunks;
let dir = TempDir::new().unwrap();
let db = store_with_chunks(&dir, 6, 4);
let hits = search_chunks(&db, &[1.0, 0.0, 0.0], 10, None).expect("search");
assert_eq!(hits.len(), 1, "one document, so one hit: {hits:?}");
assert_eq!(
hits[0].seq, 4,
"the matching chunk must be the one returned"
);
assert_eq!(hits[0].pos, 400, "chunk offset must survive the round trip");
assert!(hits[0].score > 0.9, "score was {}", hits[0].score);
}
#[test]
fn a_document_yields_at_most_one_hit() {
use crate::memory::vector_search::search_chunks;
let dir = TempDir::new().unwrap();
let db = store_with_chunks(&dir, 8, usize::MAX);
let hits = search_chunks(&db, &[0.0, 1.0, 0.0], 10, None).expect("search");
assert_eq!(
hits.len(),
1,
"8 matching chunks of one document must collapse to one result: {hits:?}"
);
}
#[test]
fn skipped_too_large_placeholders_are_never_returned() {
use crate::memory::vector_search::search_chunks;
let dir = TempDir::new().unwrap();
let db = store_with_chunks(&dir, 1, 0);
let conn = Connection::open(&db).unwrap();
conn.execute(
"UPDATE content_vectors SET model = 'skipped-too-large' WHERE hash = 'h1'",
[],
)
.unwrap();
let hits = search_chunks(&db, &[1.0, 0.0, 0.0], 10, None).expect("search");
assert!(
hits.is_empty(),
"a zero-vector placeholder must not occupy a result slot: {hits:?}"
);
}
#[test]
fn vectors_of_a_different_dimension_are_skipped() {
use crate::memory::vector_search::search_chunks;
let dir = TempDir::new().unwrap();
let db = store_with_chunks(&dir, 1, 0);
let hits = search_chunks(&db, &[1.0, 0.0, 0.0, 0.0], 10, None).expect("search");
assert!(
hits.is_empty(),
"a 3-dim stored vector must not be scored against a 4-dim query: {hits:?}"
);
}
#[test]
fn collection_scoping_excludes_other_collections() {
use crate::memory::vector_search::search_chunks;
let dir = TempDir::new().unwrap();
let db = store_with_chunks(&dir, 1, 0);
let inside = search_chunks(&db, &[1.0, 0.0, 0.0], 10, Some("memory")).expect("search");
assert_eq!(inside.len(), 1, "the document's own collection must match");
let outside = search_chunks(&db, &[1.0, 0.0, 0.0], 10, Some("brain")).expect("search");
assert!(
outside.is_empty(),
"a different collection must not return it: {outside:?}"
);
}
#[test]
fn long_content_is_split_into_overlapping_chunks() {
use crate::memory::embedding::chunks_for;
let body = "Paragraph about retrieval. ".repeat(1000); let chunks = chunks_for(&body);
assert!(
chunks.len() > 1,
"long content must produce several chunks, got {}",
chunks.len()
);
for pair in chunks.windows(2) {
assert!(
pair[1].pos > pair[0].pos,
"chunk offsets must advance: {} then {}",
pair[0].pos,
pair[1].pos
);
}
let stride = chunks[1].pos - chunks[0].pos;
assert!(
stride < chunks[0].text.len(),
"consecutive chunks must overlap: stride {stride} vs chunk len {}",
chunks[0].text.len()
);
}
#[test]
fn short_content_stays_one_chunk() {
use crate::memory::embedding::chunks_for;
let chunks = chunks_for("A short memory note.");
assert_eq!(chunks.len(), 1);
assert_eq!(chunks[0].pos, 0);
}
#[test]
fn no_chunk_approaches_the_embed_size_guard() {
use crate::memory::embedding::chunks_for;
let body = "x".repeat(500_000);
for (i, chunk) in chunks_for(&body).into_iter().enumerate() {
assert!(
chunk.text.len() < 32_000,
"chunk {i} is {} bytes, at or over the guard",
chunk.text.len()
);
}
}
#[test]
fn a_document_narrows_to_its_best_chunk() {
use crate::memory::chunk_fts::best_chunk;
let filler = "Unrelated background prose about scheduling and formatting. ".repeat(120);
let body =
format!("{filler}\n\nThe quota breaker trips after five consecutive refusals.\n\n{filler}");
let (pos, chunk) = best_chunk(&body, "quota breaker consecutive refusals").expect("a chunk");
assert!(
chunk.contains("quota breaker trips"),
"the returned chunk must contain the answer, got: {}",
&chunk[..chunk.len().min(120)]
);
assert!(
chunk.len() < body.len() / 2,
"narrowing must actually narrow: chunk {} vs body {}",
chunk.len(),
body.len()
);
assert!(pos > 0, "the answer was past the first chunk, so pos > 0");
}
#[test]
fn a_single_chunk_document_needs_no_narrowing() {
use crate::memory::chunk_fts::best_chunk;
assert!(best_chunk("A short note about quotas.", "quotas").is_none());
}
#[test]
fn empty_content_yields_no_chunk() {
use crate::memory::chunk_fts::best_chunk;
assert!(best_chunk("", "anything").is_none());
assert!(best_chunk(" \n\n ", "anything").is_none());
}
#[test]
fn chunk_refinement_folds_accents_like_the_rest_of_retrieval() {
use crate::memory::chunk_fts::best_chunk;
let filler = "Texto de relleno sobre otros temas del sistema. ".repeat(120);
let body =
format!("{filler}\n\nLa configuración del entorno se revisa cada semana.\n\n{filler}");
let (_, chunk) = best_chunk(&body, "configuracion del entorno").expect("a chunk");
assert!(
chunk.contains("configuración del entorno"),
"an unaccented query must reach the accented passage"
);
}
#[test]
fn no_production_path_writes_a_skipped_placeholder() {
let src = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
let mut offenders = Vec::new();
fn walk(dir: &std::path::Path, offenders: &mut Vec<String>) {
let Ok(entries) = std::fs::read_dir(dir) else {
return;
};
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
let name = entry.file_name();
if name == "tests" || name == "benches" {
continue;
}
walk(&path, offenders);
} else if path.extension().is_some_and(|e| e == "rs") {
let Ok(text) = std::fs::read_to_string(&path) else {
continue;
};
for (i, line) in text.lines().enumerate() {
if line.contains("insert_embedding") && line.contains("skipped-too-large") {
offenders.push(format!("{}:{}", path.display(), i + 1));
}
}
}
}
}
walk(&src, &mut offenders);
assert!(
offenders.is_empty(),
"these write a skipped-too-large placeholder, which permanently excludes \
a document from embedding:\n {}",
offenders.join("\n ")
);
}