use ito_templates::project_templates::{WorktreeTemplateContext, render_project_template};
fn agents_md_bytes() -> &'static [u8] {
ito_templates::default_project_files()
.into_iter()
.find(|f| f.relative_path == "AGENTS.md")
.expect("AGENTS.md should exist in project templates")
.contents
}
fn skill_md_bytes() -> &'static [u8] {
ito_templates::skills_files()
.into_iter()
.find(|f| f.relative_path == "ito-using-git-worktrees/SKILL.md")
.expect("ito-using-git-worktrees/SKILL.md should exist in skill templates")
.contents
}
fn render_text(template: &[u8], ctx: &WorktreeTemplateContext) -> String {
let bytes = render_project_template(template, ctx).expect("template should render");
String::from_utf8(bytes).expect("rendered output should be UTF-8")
}
const DISCOVERY_HEURISTICS: &[&str] = &[
"grep AGENTS.md",
"grep -i \"worktree",
"grep CLAUDE.md",
"No worktree directory found",
"Check Existing Directories",
"Check AGENTS.md or CLAUDE.md",
"Check CLAUDE.md",
"ls -d ../ito-worktrees",
"ls -d .worktrees",
];
fn assert_no_absolute_project_root(text: &str, project_root: &str, label: &str) {
assert!(
!text.contains(project_root),
"{label}: should not embed machine-specific absolute project_root '{project_root}'"
);
}
fn assert_no_discovery_heuristics(text: &str, label: &str) {
for heuristic in DISCOVERY_HEURISTICS {
assert!(
!text.contains(heuristic),
"{label}: should not contain discovery heuristic '{heuristic}'"
);
}
}
fn checkout_subdir_ctx() -> WorktreeTemplateContext {
WorktreeTemplateContext {
enabled: true,
strategy: "checkout_subdir".to_string(),
layout_dir_name: "ito-worktrees".to_string(),
integration_mode: "commit_pr".to_string(),
default_branch: "main".to_string(),
project_root: "/home/user/project".to_string(),
}
}
fn checkout_siblings_ctx() -> WorktreeTemplateContext {
WorktreeTemplateContext {
enabled: true,
strategy: "checkout_siblings".to_string(),
layout_dir_name: "wt".to_string(),
integration_mode: "merge_parent".to_string(),
default_branch: "develop".to_string(),
project_root: "/home/user/project".to_string(),
}
}
fn bare_control_siblings_ctx() -> WorktreeTemplateContext {
WorktreeTemplateContext {
enabled: true,
strategy: "bare_control_siblings".to_string(),
layout_dir_name: "ito-worktrees".to_string(),
integration_mode: "commit_pr".to_string(),
default_branch: "main".to_string(),
project_root: "/home/user/project".to_string(),
}
}
fn disabled_ctx() -> WorktreeTemplateContext {
WorktreeTemplateContext::default()
}
#[test]
fn agents_md_checkout_subdir() {
let ctx = checkout_subdir_ctx();
let text = render_text(agents_md_bytes(), &ctx);
assert!(text.contains("## Worktree Workflow"));
assert!(text.contains("**Strategy:** `checkout_subdir`"));
assert!(text.contains("git worktree add \".ito-worktrees/<change-name>\" -b <change-name>"));
assert!(!text.contains("{{"), "should not contain raw jinja");
assert_no_discovery_heuristics(&text, "agents_md_checkout_subdir");
assert!(
text.contains(".ito-worktrees/<change-name>/"),
"should show repo-relative worktree path"
);
assert_no_absolute_project_root(&text, &ctx.project_root, "agents_md_checkout_subdir");
}
#[test]
fn agents_md_checkout_siblings() {
let ctx = checkout_siblings_ctx();
let text = render_text(agents_md_bytes(), &ctx);
assert!(text.contains("**Strategy:** `checkout_siblings`"));
assert!(
text.contains("git worktree add \"../<project-name>-wt/<change-name>\" -b <change-name>")
);
assert!(!text.contains("{{"), "should not contain raw jinja");
assert_no_discovery_heuristics(&text, "agents_md_checkout_siblings");
assert!(
text.contains("../<project-name>-wt/<change-name>/"),
"should show repo-relative sibling worktree path"
);
assert_no_absolute_project_root(&text, &ctx.project_root, "agents_md_checkout_siblings");
}
#[test]
fn agents_md_bare_control_siblings() {
let ctx = bare_control_siblings_ctx();
let text = render_text(agents_md_bytes(), &ctx);
assert!(text.contains("**Strategy:** `bare_control_siblings`"));
assert!(text.contains(".bare/"));
assert!(text.contains("ito-worktrees/"));
assert!(
text.contains("git worktree add \"../ito-worktrees/<change-name>\" -b <change-name> main")
);
assert!(text.contains("Do not create them from the bare/control repo placeholder `HEAD`"));
assert!(!text.contains("{{"), "should not contain raw jinja");
assert_no_discovery_heuristics(&text, "agents_md_bare_control_siblings");
let layout_line = text
.lines()
.find(|l| l.contains("# bare/control repo"))
.expect("should contain bare/control repo layout line");
assert!(
layout_line.contains("../"),
"should show repo-relative bare/control layout"
);
assert_no_absolute_project_root(&text, &ctx.project_root, "agents_md_bare_control_siblings");
}
#[test]
fn agents_md_disabled() {
let text = render_text(agents_md_bytes(), &disabled_ctx());
assert!(text.contains("Worktrees are not configured for this project."));
assert!(text.contains("Do NOT create git worktrees by default."));
assert!(!text.contains("{{"), "should not contain raw jinja");
assert_no_discovery_heuristics(&text, "agents_md_disabled");
}
#[test]
fn skill_checkout_subdir() {
let ctx = checkout_subdir_ctx();
let text = render_text(skill_md_bytes(), &ctx);
assert!(text.contains("**Configured strategy:** `checkout_subdir`"));
assert!(text.contains("git worktree add \".ito-worktrees/<change-name>\" -b <change-name>"));
assert!(!text.contains("{{"), "should not contain raw jinja");
assert_no_discovery_heuristics(&text, "skill_checkout_subdir");
assert_no_absolute_project_root(&text, &ctx.project_root, "skill_checkout_subdir");
}
#[test]
fn skill_checkout_siblings() {
let ctx = checkout_siblings_ctx();
let text = render_text(skill_md_bytes(), &ctx);
assert!(text.contains("**Configured strategy:** `checkout_siblings`"));
assert!(
text.contains("git worktree add \"../<project-name>-wt/<change-name>\" -b <change-name>")
);
assert!(!text.contains("{{"), "should not contain raw jinja");
assert_no_discovery_heuristics(&text, "skill_checkout_siblings");
assert_no_absolute_project_root(&text, &ctx.project_root, "skill_checkout_siblings");
}
#[test]
fn skill_bare_control_siblings() {
let ctx = bare_control_siblings_ctx();
let text = render_text(skill_md_bytes(), &ctx);
assert!(text.contains("**Configured strategy:** `bare_control_siblings`"));
assert!(text.contains("ito-worktrees/"));
assert!(
text.contains("git worktree add \"../ito-worktrees/<change-name>\" -b <change-name> main")
);
assert!(
text.contains("Never use the bare/control repo placeholder `HEAD` as the checkout source")
);
assert!(!text.contains("{{"), "should not contain raw jinja");
assert_no_discovery_heuristics(&text, "skill_bare_control_siblings");
assert_no_absolute_project_root(&text, &ctx.project_root, "skill_bare_control_siblings");
}
#[test]
fn skill_disabled() {
let text = render_text(skill_md_bytes(), &disabled_ctx());
assert!(text.contains("Worktrees are not configured for this project."));
assert!(text.contains("Work in the current checkout."));
assert!(!text.contains("{{"), "should not contain raw jinja");
assert_no_discovery_heuristics(&text, "skill_disabled");
}