use super::*;
use crate::delivery::Repo;
use std::fs;
use tempfile::TempDir;
pub fn project() -> (TempDir, PathBuf, Project) {
let tmp = TempDir::new().unwrap();
let root = tmp.path().join("proj");
fs::create_dir(&root).unwrap();
let g = |args: &[&str]| Project::run(&root, args).unwrap();
g(&["init", "-q", "-b", "main"]);
g(&["config", "user.name", "test"]);
g(&["config", "user.email", "test@example.com"]);
g(&["config", "core.hooksPath", root.join(".git/hooks").to_str().unwrap()]);
fs::write(root.join("seed.txt"), "seed\n").unwrap();
g(&["add", "-A"]);
g(&["commit", "-q", "-m", "seed"]);
(tmp, root.clone(), Project::at(&root))
}
pub fn tip(root: &Path) -> String {
Project::run(root, &["log", "-1", "--format=%s", "main"]).unwrap().trim().to_string()
}
#[test]
fn materialize_creates_then_is_idempotent_then_reattaches_an_existing_branch() {
let (tmp, root, p) = project();
let wt = tmp.path().join("wt");
p.materialize(&wt, "work/bl-x").unwrap();
assert!(wt.join("seed.txt").exists());
assert!(p.branch_exists("work/bl-x").unwrap());
p.materialize(&wt, "work/bl-x").unwrap();
p.release(&wt).unwrap();
assert!(!wt.exists() && p.branch_exists("work/bl-x").unwrap());
p.materialize(&wt, "work/bl-x").unwrap();
assert!(wt.join("seed.txt").exists());
let _ = root;
}
#[test]
fn materialize_recovers_a_deleted_dir_with_a_stale_registration() {
let (tmp, _root, p) = project();
let wt = tmp.path().join("wt");
p.materialize(&wt, "work/bl-x").unwrap();
fs::remove_dir_all(&wt).unwrap();
p.materialize(&wt, "work/bl-x").unwrap();
assert!(wt.join("seed.txt").exists());
}
#[test]
fn release_removes_a_present_worktree_and_no_ops_when_absent() {
let (tmp, _root, p) = project();
let wt = tmp.path().join("wt");
p.release(&wt).unwrap(); p.materialize(&wt, "work/bl-x").unwrap();
p.release(&wt).unwrap();
assert!(!wt.exists());
}
#[test]
fn discard_removes_the_worktree_and_deletes_the_branch() {
let (tmp, _root, p) = project();
let wt = tmp.path().join("wt");
p.materialize(&wt, "work/bl-x").unwrap();
p.discard(&wt, "work/bl-x").unwrap();
assert!(!wt.exists());
assert!(!p.branch_exists("work/bl-x").unwrap());
p.discard(&wt, "work/bl-x").unwrap();
}
#[test]
fn mint_creates_the_target_ref_at_its_base_then_declines_to_move_it() {
let (_tmp, root, p) = project();
p.mint("work/bl-epic", "main").unwrap();
let head = |rev: &str| Project::run(&root, &["rev-parse", rev]).unwrap().trim().to_string();
assert_eq!(head("work/bl-epic"), head("main"));
fs::write(root.join("moved.txt"), "1\n").unwrap();
Project::run(&root, &["add", "-A"]).unwrap();
Project::run(&root, &["commit", "-q", "-m", "main moved"]).unwrap();
p.mint("work/bl-epic", "main").unwrap(); assert_ne!(head("work/bl-epic"), head("main"));
}
#[test]
fn integration_is_the_project_head_branch() {
let (_tmp, _root, p) = project();
assert_eq!(p.integration().unwrap(), "main");
}
#[test]
fn is_git_repo_holds_for_a_worktree_and_a_bare_repo_but_not_a_plain_dir() {
let (_tmp, _root, p) = project();
assert!(p.is_git_repo().unwrap());
let bare = TempDir::new().unwrap();
Project::run(bare.path(), &["init", "-q", "--bare", "-b", "main"]).unwrap();
assert!(Project::at(bare.path()).is_git_repo().unwrap());
let plain = TempDir::new().unwrap();
assert!(!Project::at(plain.path()).is_git_repo().unwrap());
}
#[test]
fn a_git_failure_surfaces_with_stderr() {
let outside = TempDir::new().unwrap(); let err = Project::run(outside.path(), &["rev-parse", "HEAD"]).unwrap_err();
assert!(err.to_string().starts_with("git rev-parse HEAD"), "{err}");
}
#[test]
fn root_commit_is_the_seed_root_or_none_off_a_non_repo() {
let (_tmp, root, p) = project();
let r = p.root_commit().expect("a committed repo has a root");
let seed = Project::run(&root, &["rev-parse", "HEAD"]).unwrap().trim().to_string();
assert_eq!(r, seed, "the sole commit is the root");
assert_eq!(p.root_commits(), vec![seed]);
let outside = TempDir::new().unwrap(); assert!(Project::at(outside.path()).root_commit().is_none());
assert!(Project::at(outside.path()).root_commits().is_empty());
}