use std::time::Duration;
use super::*;
fn workspace_with_sources() -> tempfile::TempDir {
let dir = tempfile::tempdir().expect("tempdir");
std::fs::write(dir.path().join("alpha.rs"), "pub fn alpha() -> u32 { 1 }\n").expect("write alpha");
std::fs::write(dir.path().join("beta.rs"), "pub fn beta() -> u32 { 2 }\n").expect("write beta");
dir
}
#[test]
fn rescan_indexes_sources_and_is_idempotent() {
store::init_isolated_cache();
let pool = WorkspacePool::new(DEFAULT_HOT_CAP);
let ws = workspace_with_sources();
let first = pool.rescan(ws.path(), None, false, false).expect("first scan");
assert_eq!(first.scanned, 2, "both sources considered");
assert_eq!(first.updated, 2, "both sources newly indexed");
let second = pool.rescan(ws.path(), None, false, false).expect("second scan");
assert_eq!(second.scanned, 2, "both sources still considered");
assert_eq!(second.updated, 0, "nothing changed on the second pass");
assert_eq!(second.skipped_unchanged, 2, "both sources skipped as unchanged");
}
#[test]
fn lru_eviction_keeps_only_the_most_recent_within_the_cap() {
store::init_isolated_cache();
let pool = WorkspacePool::new(1);
let ws1 = workspace_with_sources();
let ws2 = workspace_with_sources();
pool.rescan(ws1.path(), None, false, false).expect("scan ws1");
assert_eq!(pool.len(), 1);
pool.rescan(ws2.path(), None, false, false).expect("scan ws2");
assert_eq!(pool.len(), 1, "cap of 1 holds a single hot workspace");
let hot = pool.accessed();
assert_eq!(hot.len(), 1);
assert_eq!(hot[0].root, ws2.path(), "the most-recently-used workspace survived");
}
#[test]
fn evicted_workspace_lazily_reopens_with_its_committed_index_intact() {
store::init_isolated_cache();
let pool = WorkspacePool::new(1);
let ws1 = workspace_with_sources();
let ws2 = workspace_with_sources();
pool.rescan(ws1.path(), None, false, false).expect("scan ws1");
let hot_files = pool
.with_workspace(ws1.path(), |store| store.index.files.len())
.expect("read ws1 while hot");
assert_eq!(hot_files, 2, "ws1's two sources are indexed while it is hot");
pool.rescan(ws2.path(), None, false, false).expect("scan ws2");
assert_eq!(pool.len(), 1, "cap of 1 holds a single hot workspace");
assert!(
pool.accessed().iter().all(|w| w.root != ws1.path()),
"ws1 must have been evicted from the hot set"
);
let recovered = pool
.with_workspace(ws1.path(), |store| {
(
store.index.files.len(),
store.lookup("alpha.rs").is_some(),
store.lookup("beta.rs").is_some(),
)
})
.expect("reopen evicted ws1");
assert_eq!(
recovered,
(2, true, true),
"the reopened workspace recovers its indexed files from disk without a rescan"
);
}
#[test]
fn accessed_reports_the_hot_set() {
store::init_isolated_cache();
let pool = WorkspacePool::new(DEFAULT_HOT_CAP);
let ws = workspace_with_sources();
pool.rescan(ws.path(), None, false, false).expect("scan");
let hot = pool.accessed();
assert_eq!(hot.len(), 1);
assert_eq!(hot[0].root, ws.path());
assert_eq!(hot[0].key, store::workspace_key(ws.path()));
}
#[cfg(feature = "code-search")]
#[test]
fn embed_pass_reprocesses_the_chunk_only_sidecar_the_deferred_pass_left() {
store::init_isolated_cache();
let pool = WorkspacePool::new(DEFAULT_HOT_CAP);
let dir = tempfile::tempdir().expect("tempdir");
std::fs::write(
dir.path().join("basemind.toml"),
"\"$schema\" = \"v1\"\n[code_search]\nembed = true\n",
)
.expect("write config");
std::fs::write(dir.path().join("lib.rs"), "pub fn embed_marker() -> u32 { 42 }\n").expect("write source");
let deferred = pool.rescan(dir.path(), None, false, false).expect("deferred scan");
assert!(
deferred.updated >= 1,
"the source is newly indexed by the deferred pass"
);
let deferred_again = pool.rescan(dir.path(), None, false, false).expect("deferred rescan");
assert_eq!(deferred_again.updated, 0, "a second deferred pass changes nothing");
let embed = pool.rescan(dir.path(), None, false, true).expect("inline embed scan");
assert!(
embed.updated >= 1,
"the embed pass must re-process the chunk-only source to fill vectors (got updated={}, \
the idempotent deferred pass got {})",
embed.updated,
deferred_again.updated
);
}
#[cfg(feature = "documents")]
#[test]
fn embed_pass_indexes_a_document_the_deferred_pass_leaves_untracked() {
store::init_isolated_cache();
let pool = WorkspacePool::new(DEFAULT_HOT_CAP);
let dir = tempfile::tempdir().expect("tempdir");
std::fs::write(
dir.path().join("notes.svg"),
br#"<svg xmlns="http://www.w3.org/2000/svg"><text>photosynthesis chloroplast glucose oxygen</text></svg>"#,
)
.expect("write document");
let deferred = pool.rescan(dir.path(), None, false, false).expect("deferred scan");
assert!(
deferred.docs_indexed >= 1,
"the .svg file must route to the document tier (docs_indexed={})",
deferred.docs_indexed
);
let tracked_after_deferred = pool
.with_workspace(dir.path(), |store| store.lookup_doc("notes.svg").is_some())
.expect("read after deferred");
assert!(
!tracked_after_deferred,
"the deferred pass must not persist a document embedding entry"
);
pool.rescan(dir.path(), None, false, true).expect("inline embed scan");
let tracked_after_inline = pool
.with_workspace(dir.path(), |store| store.lookup_doc("notes.svg").is_some())
.expect("read after inline");
assert!(
tracked_after_inline,
"the inline embed pass must index the document so search_documents can reach it"
);
}
#[test]
fn evict_idle_zero_drops_every_entry() {
store::init_isolated_cache();
let pool = WorkspacePool::new(DEFAULT_HOT_CAP);
let ws = workspace_with_sources();
pool.rescan(ws.path(), None, false, false).expect("scan");
assert_eq!(pool.len(), 1);
let dropped = pool.evict_idle(Duration::ZERO);
assert_eq!(dropped, 1, "a zero idle window evicts everything");
assert_eq!(pool.len(), 0);
}