use super::{DriveOutcome, drive};
use crate::prompt::SystemClock;
use crate::prompt::inbox::{deposit, inbox_dir, try_acquire};
use crate::template::{GitRunner, RealGit};
use crate::workspace::{agent_ref, fixture, repo_git};
use std::path::{Path, PathBuf};
use tempfile::TempDir;
const AGENT: &str = "20260101-a1";
fn workspace() -> (TempDir, PathBuf) {
let (holder, ws) = fixture::workspace();
fixture::spawn_root(&ws, AGENT);
(holder, ws)
}
fn tip(ws: &Path) -> String {
RealGit::new()
.run_capture(&repo_git(ws), &["rev-parse", &agent_ref(AGENT)])
.unwrap()
}
#[test]
fn a_held_lock_means_already_driven() {
let (_h, ws) = workspace();
let ws = ws.as_path();
let _held = try_acquire(&inbox_dir(ws, AGENT)).unwrap().expect("free");
let outcome = drive(ws, AGENT, &RealGit::new()).unwrap();
assert_eq!(outcome, DriveOutcome::AlreadyDriven);
}
#[test]
fn empty_inbox_exits_silently_without_stepping_or_relaunching() {
let (_h, ws) = workspace();
let ws = ws.as_path();
let before = tip(ws);
let outcome = drive(ws, AGENT, &RealGit::new()).unwrap();
assert_eq!(outcome, DriveOutcome::NothingToDeliver);
assert_eq!(tip(ws), before, "no step: the tip must not move");
let entries: Vec<_> = std::fs::read_dir(ws.join("inbox"))
.unwrap()
.flatten()
.map(|e| e.file_name().to_string_lossy().into_owned())
.collect();
assert_eq!(entries, vec![AGENT.to_string()]);
assert_eq!(std::fs::read_dir(inbox_dir(ws, AGENT)).unwrap().count(), 0);
}
#[test]
fn a_late_deposit_is_delivered_by_the_launched_driver() {
let (_h, ws) = workspace();
let ws = ws.as_path();
deposit(ws, AGENT, "user", "late mail", &SystemClock).unwrap();
let outcome = drive(ws, AGENT, &RealGit::new()).unwrap();
assert_eq!(outcome, DriveOutcome::Delivered(1));
assert_eq!(std::fs::read_dir(inbox_dir(ws, AGENT)).unwrap().count(), 0);
let shown = RealGit::new()
.run_capture(
&repo_git(ws),
&[
"show",
&format!("{}:messages/001-user.md", agent_ref(AGENT)),
],
)
.unwrap();
assert!(shown.contains("late mail"), "got {shown:?}");
}
#[test]
fn a_torn_down_worktree_is_rematerialized_before_delivery() {
let (_h, ws) = workspace();
let ws = ws.as_path();
let repo = repo_git(ws);
let wt = crate::workspace::agent_worktree(ws, AGENT);
let wt_str = wt.to_string_lossy().to_string();
RealGit::new()
.run(&repo, &["worktree", "remove", wt_str.as_str()])
.unwrap();
assert!(!wt.exists());
deposit(ws, AGENT, "user", "wake up", &SystemClock).unwrap();
let outcome = drive(ws, AGENT, &RealGit::new()).unwrap();
assert_eq!(outcome, DriveOutcome::Delivered(1));
assert!(wt.join("messages").join("001-user.md").exists());
}
#[test]
fn rematerialize_failure_is_surfaced_as_a_git_error() {
let (_h, ws) = workspace();
let ws = ws.as_path();
let repo = repo_git(ws);
let wt = crate::workspace::agent_worktree(ws, AGENT);
let wt_str = wt.to_string_lossy().to_string();
let g = RealGit::new();
g.run(&repo, &["worktree", "remove", wt_str.as_str()])
.unwrap();
g.run(&repo, &["branch", "-D", &agent_ref(AGENT)]).unwrap();
deposit(ws, AGENT, "user", "orphaned", &SystemClock).unwrap();
let err = drive(ws, AGENT, &RealGit::new()).unwrap_err();
assert!(
matches!(
err,
crate::prompt::Error::Git {
op: "worktree add (rematerialize)",
..
}
),
"{err}"
);
}
#[test]
fn a_broken_inbox_surfaces_as_an_executor_lock_error() {
let (_h, ws) = workspace();
let ws = ws.as_path();
std::fs::create_dir_all(ws.join("inbox")).unwrap();
std::fs::write(inbox_dir(ws, AGENT), b"not a dir").unwrap();
let err = drive(ws, AGENT, &RealGit::new()).unwrap_err();
assert!(
matches!(err, crate::prompt::Error::ExecutorLock { .. }),
"{err}"
);
}