use super::*;
#[test]
fn help_render_matches_spawned_binary() {
for args in [
vec!["clone", "--help"],
vec!["capture", "--help"],
vec!["capture", "--help-agent"],
vec!["push", "--help"],
vec!["log", "--help"],
vec!["bridge", "git", "import", "--help"],
vec!["import", "--help"],
vec!["help"],
vec!["help", "advanced"],
vec!["help", "git-concepts"],
vec!["help", "threads"],
vec!["help", "git-overlay"],
vec!["help", "status"],
vec!["-vC", ".", "capture", "--help-agent"],
vec!["--output", "text", "capture", "--help-agent"],
] {
let spawned = heddle(&args, None)
.unwrap_or_else(|err| panic!("spawned `heddle {}`: {err}", args.join(" ")));
let in_process = heddle_help(&args);
assert_eq!(
in_process,
spawned,
"in-process render of `heddle {}` must match the spawned binary's stdout byte-for-byte",
args.join(" ")
);
}
}
#[test]
fn clone_help_pins_behavior_stanza() {
let summary = heddle_help(&["clone", "--help"]);
assert!(
summary.contains("Behavior:"),
"clone help should include a Behavior stanza: {summary}"
);
assert!(
summary.contains("default branch") && summary.contains("`main`"),
"clone help summary should say where clones land: {summary}"
);
assert!(
summary.contains("--thread"),
"clone help summary should name the escape hatch: {summary}"
);
assert!(
summary.contains("heddle help clone"),
"clone help should breadcrumb to the full clone topic: {summary}"
);
let help = heddle_help(&["help", "clone"]);
assert!(
help.contains("no --thread") && help.contains("default branch"),
"clone topic should explain where an unhinted clone lands: {help}"
);
assert!(
help.contains("`main`") && help.contains("alphabetically first"),
"clone topic should name the default-thread fallback chain: {help}"
);
assert!(
help.contains("Native-local and hosted Heddle clones")
&& help.contains("target `main` directly"),
"clone topic should distinguish the Heddle-remote default from the Git-overlay chain: {help}"
);
assert!(
help.contains("the clone fails") && help.contains("--thread <name>"),
"clone topic should document that an unhinted native/hosted clone fails when the remote has no `main` thread: {help}"
);
assert!(
help.contains("--depth 0") && help.contains("full history"),
"clone topic should explain that --depth 0 is full history: {help}"
);
assert!(
help.contains("depth boundary") && help.contains("re-clone at a greater --depth"),
"clone topic should explain that history past the depth boundary is absent and recovered by re-cloning at a greater depth: {help}"
);
assert!(
help.contains("Git-overlay clones reject a nonzero --depth")
&& help.contains("--depth 0 is accepted"),
"clone topic should distinguish nonzero --depth (rejected) from --depth 0 (accepted) for Git-overlay clones: {help}"
);
assert!(
help.contains("tip plus its immediate parents"),
"clone topic should explain that --depth 1 keeps the tip plus immediate parents: {help}"
);
assert!(
help.contains("heddle help threads"),
"clone topic should point at the threads topic: {help}"
);
}
#[test]
fn capture_help_agent_reveals_hidden_flags_through_clap() {
let expect_revealed = |help: &str, ctx: &str| {
for flag in [
"--agent-provider",
"--agent-model",
"--agent-session",
"--agent-segment",
"--policy",
"--no-policy",
"--no-agent",
"--split",
"--into",
] {
assert!(
help.contains(flag),
"`{ctx}` should reveal `{flag}` inline: {help}"
);
}
};
let plain = heddle_help(&["capture", "--help-agent"]);
expect_revealed(&plain, "capture --help-agent");
let clustered = heddle_help(&["-vC", ".", "capture", "--help-agent"]);
expect_revealed(&clustered, "-vC <path> capture --help-agent");
let long_global = heddle_help(&["--output", "text", "capture", "--help-agent"]);
expect_revealed(&long_global, "--output text capture --help-agent");
}
#[test]
fn capture_help_keeps_help_agent_hidden_but_keeps_the_pointer() {
let help = heddle_help(&["capture", "--help"]);
assert!(
help.contains("heddle capture --help-agent"),
"`capture --help` should keep the after-help pointer to `--help-agent`: {help}"
);
assert_eq!(
help.matches("--help-agent").count(),
1,
"`capture --help` must not list the hidden `--help-agent` flag (only the after-help pointer): {help}"
);
}
#[test]
fn clone_help_carries_hidden_flag_breadcrumb() {
let help = heddle_help(&["clone", "--help"]);
assert!(
help.contains("Advanced/planned flags: see `heddle help clone`."),
"clone help should point to the advanced/planned flag topic: {help}"
);
assert!(
!help.contains("--lazy") && !help.contains("--filter"),
"clone help should keep hidden flags out of the human options/body: {help}"
);
}
#[test]
fn pull_help_carries_hidden_flag_breadcrumb() {
let help = heddle_help(&["pull", "--help"]);
assert!(
help.contains("--lazy") && help.contains("planned for v0.3.1"),
"pull help should name the hidden --lazy flag and its timeline: {help}"
);
}
#[test]
fn start_help_carries_hidden_flag_breadcrumb() {
let help = heddle_help(&["start", "--help"]);
for flag in [
"--agent-provider",
"--agent-model",
"--parent-thread",
"--print-cd-path",
"--daemon",
"--no-daemon",
"--shared-target",
] {
assert!(
help.contains(flag),
"start help should name the hidden `{flag}` flag in its advanced stanza: {help}"
);
}
}
#[test]
fn diff_help_warns_patch_compat_is_best_effort() {
let help = heddle_help(&["diff", "--help"]);
assert!(
help.contains("git apply") && help.contains("patch(1)") && help.contains("best-effort"),
"diff help should state that patch(1) support is best-effort and git apply is canonical: {help}"
);
assert!(
help.contains("type changes") && help.contains("mode bits"),
"diff help should name the git-extended-header cases that need git apply: {help}"
);
}
#[test]
fn merge_help_pins_semantic_default_and_opt_out() {
let help = heddle_help(&["merge", "--help"]);
assert!(
help.contains("--no-semantic"),
"merge help should expose the hunk-only opt-out: {help}"
);
assert!(
help.contains("Semantic merge is the default"),
"merge help should state that semantic merge is the default: {help}"
);
assert!(
!help.contains("--semantic"),
"merge help must not advertise the removed --semantic opt-in: {help}"
);
assert!(
!help.contains("Requires building heddle with `--features semantic`"),
"merge help must not claim the default feature requires an explicit build flag: {help}"
);
}
#[test]
fn commit_help_explains_intent_alias() {
let help = heddle_help(&["commit", "--help"]);
assert!(
help.contains("--intent") && help.contains("WHY"),
"commit help should explain the deliberate --intent alias: {help}"
);
}
#[test]
fn git_concepts_topic_maps_git_veteran_terms() {
let help = heddle_help(&["help", "git-concepts"]);
assert!(
help.contains("| Git concept | Heddle concept + semantic difference |"),
"git-concepts topic should be a two-column concept table: {help}"
);
for row in [
"`git commit`",
"Git commit SHA",
"`git branch foo`",
"`git checkout foo` / `git switch foo`",
"`git tag v1.0`",
"`git remote add origin <url>`",
"`git push` / `git pull`",
"`git fetch`",
"`git rebase` to catch up",
] {
assert!(
help.contains(row),
"git-concepts topic missing row `{row}`: {help}"
);
}
for heddle in [
"heddle commit -m",
"heddle start foo",
"heddle thread create foo",
"heddle thread marker create v1.0",
"heddle remote add origin <url>",
"heddle push",
"heddle pull",
"heddle fetch",
"heddle sync",
] {
assert!(
help.contains(heddle),
"git-concepts topic missing Heddle mapping `{heddle}`: {help}"
);
}
assert!(
help.contains("Reconciliation examples:")
&& help.contains("heddle start feature/auth --path ../feature-auth")
&& help.contains("heddle thread marker create v1.0")
&& help.contains("heddle fetch origin")
&& help.contains("heddle sync"),
"git-concepts topic should include practical reconciliation examples: {help}"
);
let top = heddle_help(&["help"]);
assert!(
top.contains("heddle help git-concepts"),
"main help should link the git concept map from Start here: {top}"
);
}