use super::{cwd_ref, read, write};
use crate::template::{GitRunner, RealGit};
use crate::workspace::{fixture, repo_git};
use std::path::{Path, PathBuf};
fn agent() -> (tempfile::TempDir, PathBuf) {
let (holder, ws) = fixture::workspace();
fixture::spawn_root(&ws, "a");
(holder, ws)
}
#[test]
fn the_mark_ref_lives_in_the_shared_per_agent_mark_namespace() {
assert_eq!(cwd_ref("a-b"), "refs/lernie/cwd/a-b");
}
#[test]
fn an_unset_mark_reads_as_none_so_the_default_applies() {
let (_h, ws) = agent();
assert_eq!(read(&ws, "a", &RealGit::new()), None);
}
#[test]
fn a_written_mark_reads_back_the_same_absolute_path() {
let (_h, ws) = agent();
let git = RealGit::new();
let dir = ws.join("agents/a");
write(&ws, "a", &dir, &git).unwrap();
assert_eq!(read(&ws, "a", &git), Some(dir));
}
#[test]
fn a_second_write_wins_because_a_cd_replaces_where_the_agent_is() {
let (_h, ws) = agent();
let git = RealGit::new();
write(&ws, "a", Path::new("/tmp"), &git).unwrap();
write(&ws, "a", Path::new("/usr"), &git).unwrap();
assert_eq!(read(&ws, "a", &git), Some(PathBuf::from("/usr")));
}
#[test]
fn the_mark_is_per_agent_so_one_agents_cd_never_moves_another() {
let (_h, ws) = agent();
let git = RealGit::new();
fixture::spawn_root(&ws, "b");
write(&ws, "a", Path::new("/tmp"), &git).unwrap();
assert_eq!(read(&ws, "b", &git), None);
}
#[test]
fn hashing_the_value_leaves_nothing_behind_beside_the_repo() {
let (_h, ws) = agent();
write(&ws, "a", Path::new("/tmp"), &RealGit::new()).unwrap();
let leftovers: Vec<_> = std::fs::read_dir(repo_git(&ws))
.unwrap()
.flatten()
.map(|e| e.file_name().to_string_lossy().into_owned())
.filter(|n| n.starts_with("cwd-mark."))
.collect();
assert!(
leftovers.is_empty(),
"staged value left behind: {leftovers:?}"
);
}
#[test]
fn a_mark_pointing_at_a_non_blob_reads_as_none_rather_than_failing_a_tool_call() {
let (_h, ws) = agent();
let git = RealGit::new();
let repo = repo_git(&ws);
let head = git.run_capture(&repo, &["rev-parse", "agents/a"]).unwrap();
git.run(&repo, &["update-ref", &cwd_ref("a"), &head])
.unwrap();
assert_eq!(read(&ws, "a", &git), None);
}
#[test]
fn a_workspace_with_no_repo_reads_as_none() {
let holder = tempfile::TempDir::new().unwrap();
assert_eq!(read(holder.path(), "a", &RealGit::new()), None);
}
#[test]
fn a_path_that_trimming_would_change_is_declined_rather_than_stored_wrong() {
let (_h, ws) = agent();
let err = write(&ws, "a", Path::new("/tmp/trailing "), &RealGit::new()).unwrap_err();
assert!(err.to_string().contains("trimmed UTF-8"), "{err}");
assert_eq!(read(&ws, "a", &RealGit::new()), None);
}
#[test]
fn an_empty_path_is_declined() {
let (_h, ws) = agent();
let err = write(&ws, "a", Path::new(""), &RealGit::new()).unwrap_err();
assert!(err.to_string().contains("trimmed UTF-8"), "{err}");
}
#[test]
fn a_non_utf8_path_is_declined() {
use std::ffi::OsStr;
use std::os::unix::ffi::OsStrExt;
let (_h, ws) = agent();
let bad = PathBuf::from(OsStr::from_bytes(b"/tmp/\xff"));
let err = write(&ws, "a", &bad, &RealGit::new()).unwrap_err();
assert!(err.to_string().contains("trimmed UTF-8"), "{err}");
}
#[test]
fn a_git_that_cannot_hash_the_value_surfaces_the_failure() {
let (_h, ws) = agent();
let bare = ws.join("nowhere");
std::fs::create_dir_all(super::repo_git(&bare)).unwrap();
let err = write(&bare, "a", Path::new("/tmp"), &RealGit::new()).unwrap_err();
assert!(err.to_string().contains("hash-object"), "{err}");
}
#[test]
fn a_value_that_cannot_be_staged_surfaces_the_io_failure() {
let holder = tempfile::TempDir::new().unwrap();
let err = write(holder.path(), "a", Path::new("/tmp"), &RealGit::new()).unwrap_err();
assert_eq!(err.kind(), std::io::ErrorKind::NotFound);
}