use std::collections::HashMap;
use modelshelf::dedup::DedupOptions;
use modelshelf::gguf::fixtures::{gguf_bytes, write_gguf};
use modelshelf::scan::fixtures;
use modelshelf::{Ecosystem, ModelId, OpenOptions, ScanEnv, ScanOptions, Shelf};
fn test_options() -> DedupOptions {
DedupOptions {
min_size_bytes: 0,
recent_grace_secs: 0,
..Default::default()
}
}
struct Fx {
_tmp: tempfile::TempDir,
shelf: Shelf,
scan: ScanOptions,
ollama_root: std::path::PathBuf,
lmstudio_root: std::path::PathBuf,
hf_root: std::path::PathBuf,
}
fn fixture() -> Fx {
let tmp = tempfile::tempdir().unwrap();
let base = tmp.path();
let (ollama_root, lmstudio_root, hf_root) =
(base.join("ollama"), base.join("lmstudio"), base.join("hf"));
for d in [&ollama_root, &lmstudio_root, &hf_root, &base.join("home")] {
std::fs::create_dir_all(d).unwrap();
}
let shelf =
Shelf::open(OpenOptions::at(base.join("shelf")).app_id("com.example.test")).unwrap();
let scan = ScanOptions {
env: ScanEnv {
home_dir: Some(base.join("home")),
roots: HashMap::from([
(Ecosystem::Ollama, ollama_root.clone()),
(Ecosystem::Lmstudio, lmstudio_root.clone()),
(Ecosystem::HfCache, hf_root.clone()),
]),
},
..Default::default()
};
Fx {
_tmp: tmp,
shelf,
scan,
ollama_root,
lmstudio_root,
hf_root,
}
}
#[test]
fn plan_never_targets_read_only_ecosystems() {
let fx = fixture();
let content = gguf_bytes("Everywhere", "llama", 15, 4096, b"EVERYWHERE");
fixtures::ollama::add_model(&fx.ollama_root, "everywhere", "latest", &content);
fixtures::hf_cache::add_model(
&fx.hf_root,
"org/Everywhere-GGUF",
"rev",
"e.gguf",
&content,
);
write_gguf(
&fx.lmstudio_root.join("org/Everywhere-GGUF/e1.gguf"),
"Everywhere",
"llama",
15,
4096,
b"EVERYWHERE",
);
write_gguf(
&fx.lmstudio_root.join("org/Everywhere-GGUF/e2.gguf"),
"Everywhere",
"llama",
15,
4096,
b"EVERYWHERE",
);
fx.shelf.scan(&fx.scan).unwrap();
let plan = fx.shelf.plan_dedup(&test_options()).unwrap();
for action in &plan.actions {
assert!(
!action.target.starts_with(&fx.ollama_root),
"plan targets Ollama internals: {}",
action.target.display()
);
assert!(
!action.target.starts_with(&fx.hf_root),
"plan targets HF cache internals: {}",
action.target.display()
);
assert!(action.ecosystem.is_writable());
}
assert_eq!(plan.actions.len(), 2);
assert_eq!(plan.adoptions.len(), 1);
}
#[cfg(unix)]
#[test]
fn apply_links_duplicates_to_store_and_undo_restores_them() {
use std::os::unix::fs::MetadataExt;
let fx = fixture();
let payload = b"DEDUP-ME-PLEASE";
let a = fx.lmstudio_root.join("org/Model-GGUF/copy-a.gguf");
let b = fx.lmstudio_root.join("org/Model-GGUF/copy-b.gguf");
write_gguf(&a, "Dedup Me", "llama", 15, 4096, payload);
write_gguf(&b, "Dedup Me", "llama", 15, 4096, payload);
fx.shelf.scan(&fx.scan).unwrap();
let before_a = std::fs::metadata(&a).unwrap().ino();
let before_b = std::fs::metadata(&b).unwrap().ino();
assert_ne!(std::fs::read(&a).unwrap().len(), 0);
assert_ne!(before_a, before_b, "test setup: independent files");
let plan = fx.shelf.plan_dedup(&test_options()).unwrap();
assert_eq!(plan.actions.len(), 2);
let content_before = std::fs::read(&a).unwrap();
let report = fx.shelf.apply_dedup(&plan).unwrap();
assert_eq!(report.applied, 2);
assert!(report.skipped.is_empty(), "{:?}", report.skipped);
let id: &ModelId = &fx.shelf.list().unwrap()[0].id;
let blob = fx.shelf.blob_path(id).unwrap();
let blob_ino = std::fs::metadata(&blob).unwrap().ino();
assert_eq!(std::fs::metadata(&a).unwrap().ino(), blob_ino);
assert_eq!(std::fs::metadata(&b).unwrap().ino(), blob_ino);
assert_eq!(std::fs::read(&a).unwrap(), content_before);
let entry = &fx.shelf.list().unwrap()[0];
let hardlinks = entry
.locations
.iter()
.filter(|l| l.role == modelshelf::LocationRole::Hardlink)
.count();
assert_eq!(hardlinks, 2);
let undo = fx.shelf.undo(&report.op_id).unwrap();
assert_eq!(undo.restored, 2);
let after_a = std::fs::metadata(&a).unwrap();
assert_ne!(after_a.ino(), blob_ino, "undo must break the link");
assert_eq!(std::fs::read(&a).unwrap(), content_before);
assert_eq!(std::fs::read(&b).unwrap(), content_before);
}
#[cfg(unix)]
#[test]
fn apply_skips_files_changed_after_planning() {
let fx = fixture();
let payload = b"WILL-CHANGE";
let a = fx.lmstudio_root.join("org/M-GGUF/a.gguf");
let b = fx.lmstudio_root.join("org/M-GGUF/b.gguf");
write_gguf(&a, "M", "llama", 15, 4096, payload);
write_gguf(&b, "M", "llama", 15, 4096, payload);
fx.shelf.scan(&fx.scan).unwrap();
let plan = fx.shelf.plan_dedup(&test_options()).unwrap();
assert_eq!(plan.actions.len(), 2);
std::fs::write(&a, b"user overwrote this with something precious").unwrap();
let precious = std::fs::read(&a).unwrap();
let report = fx.shelf.apply_dedup(&plan).unwrap();
assert_eq!(report.applied, 1, "unchanged copy still deduped");
assert_eq!(report.skipped.len(), 1);
assert_eq!(report.skipped[0].path, a);
assert_eq!(
std::fs::read(&a).unwrap(),
precious,
"changed file must remain untouched"
);
}
#[test]
fn gc_deletes_only_orphan_blobs() {
let fx = fixture();
let registered = gguf_bytes("Registered", "llama", 15, 4096, b"REGISTERED");
write_gguf(
&fx.lmstudio_root.join("o/R-GGUF/r1.gguf"),
"Registered",
"llama",
15,
4096,
b"REGISTERED",
);
write_gguf(
&fx.lmstudio_root.join("o/R-GGUF/r2.gguf"),
"Registered",
"llama",
15,
4096,
b"REGISTERED",
);
fx.shelf.scan(&fx.scan).unwrap();
let plan = fx.shelf.plan_dedup(&test_options()).unwrap();
fx.shelf.apply_dedup(&plan).unwrap();
let _ = registered;
let orphan_hex = "f".repeat(64);
let orphan = fx.shelf.paths().blob_file(&orphan_hex);
std::fs::write(&orphan, b"ORPHANED BYTES").unwrap();
let report = fx.shelf.gc(false).unwrap();
assert_eq!(report.orphan_blobs, vec![orphan.clone()]);
assert_eq!(report.deleted, 0);
assert!(orphan.exists());
assert_eq!(report.unreferenced.len(), 1, "ref-less model is reported");
let report = fx.shelf.gc(true).unwrap();
assert_eq!(report.deleted, 1);
assert!(!orphan.exists());
let entry = &fx.shelf.list().unwrap()[0];
assert!(
fx.shelf.blob_path(&entry.id).is_ok(),
"registered blob survives gc"
);
}
#[test]
fn cloud_synced_paths_are_refused() {
let fx = fixture();
let payload = b"CLOUDY";
write_gguf(
&fx.lmstudio_root.join("OneDrive/M-GGUF/a.gguf"),
"M",
"llama",
15,
4096,
payload,
);
write_gguf(
&fx.lmstudio_root.join("OneDrive/M-GGUF/b.gguf"),
"M",
"llama",
15,
4096,
payload,
);
fx.shelf.scan(&fx.scan).unwrap();
let plan = fx.shelf.plan_dedup(&test_options()).unwrap();
assert!(plan.actions.is_empty());
assert_eq!(plan.skipped.len(), 2);
assert!(plan.skipped[0].reason.contains("cloud-synced"));
}