#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
use std::cell::RefCell;
use std::path::{Path, PathBuf};
use llmenv::config::Marketplace;
use llmenv::plugins::cache::{GitBackend, sync_marketplace_with};
use tempfile::tempdir;
struct FakeGit {
source_head: RefCell<String>,
cloned: RefCell<std::collections::HashMap<PathBuf, String>>,
clone_calls: RefCell<usize>,
pull_calls: RefCell<usize>,
}
impl FakeGit {
fn new(initial_head: &str) -> Self {
Self {
source_head: RefCell::new(initial_head.to_string()),
cloned: RefCell::new(std::collections::HashMap::new()),
clone_calls: RefCell::new(0),
pull_calls: RefCell::new(0),
}
}
fn advance_source(&self, new_head: &str) {
*self.source_head.borrow_mut() = new_head.to_string();
}
}
impl GitBackend for FakeGit {
fn clone(&self, _source: &str, dest: &Path) -> anyhow::Result<()> {
*self.clone_calls.borrow_mut() += 1;
std::fs::create_dir_all(dest.join(".git"))?;
self.cloned
.borrow_mut()
.insert(dest.to_path_buf(), self.source_head.borrow().clone());
Ok(())
}
fn pull(&self, repo: &Path) -> anyhow::Result<()> {
*self.pull_calls.borrow_mut() += 1;
self.cloned
.borrow_mut()
.insert(repo.to_path_buf(), self.source_head.borrow().clone());
Ok(())
}
fn head(&self, repo: &Path) -> Option<String> {
self.cloned.borrow().get(repo).cloned()
}
}
#[test]
fn git_source_clones_into_cache_and_reports_head() {
let cache = tempdir().expect("cache");
let git = FakeGit::new("a".repeat(40).as_str());
let m = Marketplace {
name: "demo".into(),
source: "https://example.com/demo.git".into(),
};
let state = sync_marketplace_with(cache.path(), &m, false, &git).expect("sync");
assert!(state.install_location.join(".git").exists(), "cloned repo");
let cache_root = std::fs::canonicalize(cache.path()).expect("canon cache");
let install = std::fs::canonicalize(&state.install_location).expect("canon install");
assert!(
install.starts_with(&cache_root),
"clone {install:?} lives under cache {cache_root:?}"
);
let head = state.head.expect("git source reports a HEAD");
assert_eq!(head.len(), 40, "full sha");
assert_eq!(*git.clone_calls.borrow(), 1, "cloned exactly once");
}
#[test]
fn second_sync_with_refresh_fast_forwards_to_new_head() {
let cache = tempdir().expect("cache");
let git = FakeGit::new("a".repeat(40).as_str());
let m = Marketplace {
name: "demo".into(),
source: "https://example.com/demo.git".into(),
};
let first = sync_marketplace_with(cache.path(), &m, false, &git).expect("first sync");
git.advance_source("b".repeat(40).as_str());
let second = sync_marketplace_with(cache.path(), &m, true, &git).expect("refresh sync");
assert_ne!(
first.head, second.head,
"refresh should advance HEAD to the new source commit"
);
assert_eq!(*git.clone_calls.borrow(), 1, "no re-clone on refresh");
assert_eq!(*git.pull_calls.borrow(), 1, "refresh pulls once");
}
#[test]
fn refresh_false_does_not_advance_existing_clone() {
let cache = tempdir().expect("cache");
let git = FakeGit::new("a".repeat(40).as_str());
let m = Marketplace {
name: "demo".into(),
source: "https://example.com/demo.git".into(),
};
let first = sync_marketplace_with(cache.path(), &m, false, &git).expect("first sync");
git.advance_source("b".repeat(40).as_str());
let second = sync_marketplace_with(cache.path(), &m, false, &git).expect("no-refresh sync");
assert_eq!(
first.head, second.head,
"without refresh the existing clone is reused as-is"
);
assert_eq!(*git.pull_calls.borrow(), 0, "no-refresh never pulls");
}
#[test]
fn ext_transport_source_is_rejected() {
let cache = tempdir().expect("cache");
let git = FakeGit::new("a".repeat(40).as_str());
let m = Marketplace {
name: "evil".into(),
source: "ext::sh -c id".into(),
};
let err =
sync_marketplace_with(cache.path(), &m, false, &git).expect_err("ext:: must be rejected");
assert!(
format!("{err:#}").contains("disallowed git transport"),
"unexpected error: {err:#}"
);
assert_eq!(*git.clone_calls.borrow(), 0, "backend never invoked");
}
#[test]
fn fd_transport_source_is_rejected() {
let cache = tempdir().expect("cache");
let git = FakeGit::new("a".repeat(40).as_str());
let m = Marketplace {
name: "evil".into(),
source: "fd::17".into(),
};
let err =
sync_marketplace_with(cache.path(), &m, false, &git).expect_err("fd:: must be rejected");
assert!(
format!("{err:#}").contains("disallowed git transport"),
"unexpected error: {err:#}"
);
assert_eq!(*git.clone_calls.borrow(), 0, "backend never invoked");
}
#[test]
fn local_path_source_used_in_place_without_clone() {
let src = tempdir().expect("src");
let cache = tempdir().expect("cache");
let git = FakeGit::new("a".repeat(40).as_str());
let m = Marketplace {
name: "demo".into(),
source: src.path().to_string_lossy().into_owned(),
};
let state = sync_marketplace_with(cache.path(), &m, false, &git).expect("sync");
assert_eq!(state.head, None, "path sources carry no HEAD token");
assert_eq!(
std::fs::canonicalize(&state.install_location).expect("canon"),
std::fs::canonicalize(src.path()).expect("canon src"),
"path source resolves to the source itself, not a cache clone"
);
assert_eq!(*git.clone_calls.borrow(), 0, "path source never clones");
}