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!["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("Native clones follow the remote default thread")
&& summary.contains("`--thread` overrides")
&& summary.contains("Git clones check out the selected default branch")
&& !summary.contains("and checks out the selected default branch"),
"clone help summary should name the native default thread and keep Git default-branch language: {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("Git source is streamed by Sley directly")
&& help.contains("No Git executable or `.heddle/git` mirror is used"),
"clone topic should explain the Git-overlay transport and storage path: {help}"
);
assert!(
help.contains("Native clones follow the remote default thread")
&& help.contains("pass `--thread <name>`")
&& help.contains("to override")
&& !help.contains("Native clones target `main`")
&& !help.contains("if the remote has no `main` thread"),
"clone topic should explain native default-thread selection: {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 ingest full history")
&& help.contains("reject partial-history")
&& help.contains("options"),
"clone topic should explain that shallow history is native-only: {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_marks_intent_required_on_the_usage_line() {
let help = heddle_help(&["capture", "--help"]);
let usage = help
.lines()
.find(|line| line.contains("Usage:"))
.unwrap_or(help.as_str());
assert!(
usage.contains("-m") && usage.contains("<INTENT>") && !usage.contains("[-m"),
"capture usage must mark -m required so agents do not guess a default intent: {help}"
);
}
#[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("hydrates it explicitly later"),
"pull help should name the hidden --lazy flag and its behavior: {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",
"--no-shared-target",
] {
assert!(
help.contains(flag),
"start help should name the hidden `{flag}` flag in its advanced stanza: {help}"
);
}
}
#[test]
fn diff_help_explains_git_compatible_patch_headers() {
let help = heddle_help(&["diff", "--help"]);
assert!(
help.contains("Git-compatible unified diff")
&& help.contains("extended headers for type and mode changes"),
"diff help should explain its patch format and extended headers: {help}"
);
}
#[test]
fn restore_story_is_documented_and_start_requires_path() {
let diff = heddle_help(&["diff", "--help"]);
assert!(
diff.contains("Heddle does not restore one file")
&& diff.contains("heddle undo --recover")
&& diff.contains("heddle revert"),
"diff help must document the restore story: {diff}"
);
let revert = heddle_help(&["revert", "--help"]);
assert!(
revert.contains("no restore/checkout/reset")
&& revert.contains("heddle start")
&& revert.contains("heddle undo --recover"),
"revert help must say single-file restore is unavailable: {revert}"
);
let undo = heddle_help(&["undo", "--help"]);
assert!(
undo.contains("heddle undo --hard --preview")
&& undo.contains("last undo")
&& undo.contains("no restore/checkout/reset"),
"undo help must document --hard --preview and the restore limit: {undo}"
);
let start = heddle_help(&["start", "--help"]);
assert!(
start.contains("`--path` is required")
&& start.contains(".heddle/threads/")
&& start.contains("`--workspace auto`")
&& start.contains("heddle thread create"),
"start help must say --path is required for omitted and auto workspace: {start}"
);
let model = heddle_help(&["help", "model"]);
assert!(
model.contains("Heddle does not restore one file")
&& model.contains("heddle undo --recover"),
"help model must document the restore story: {model}"
);
}
#[test]
fn commit_help_explains_capture_boundary() {
let help = heddle_help(&["commit", "--help"]);
assert!(
help.contains("Defaults to the current capture intent")
&& help.contains("Commits the complete captured tree")
&& help.contains("Git pre-commit and commit-msg hooks are not run"),
"commit help should explain how a capture becomes Git history: {help}"
);
}
#[test]
fn git_concepts_topic_explains_authority_and_current_surface() {
let help = heddle_help(&["help", "git-concepts"]);
assert!(
help.contains("Git and Heddle own different layers."),
"git-concepts topic should lead with the authority boundary: {help}"
);
for ownership in [
"`.git` owns commits, refs",
"the index, and worktree state",
"coordination and durable metadata in `.heddle`",
"captures, provenance, threads, readiness, review, and safe landing",
] {
assert!(
help.contains(ownership),
"git-concepts topic missing ownership statement `{ownership}`: {help}"
);
}
assert!(
help.contains("`clone`,")
&& help.contains("`commit`,")
&& help.contains("`pull`,")
&& help.contains("`push`, and `remote`")
&& help.contains("embedded Sley engine directly"),
"git-concepts topic should name the current thin Git surface and engine: {help}"
);
for mapping in [
"| Intent | Git Overlay | Native Heddle |",
"`heddle capture`, then `heddle commit`",
"| Record a granular Heddle savepoint | `heddle capture` | `heddle capture` |",
"| Check integration readiness | `heddle ready` | `heddle ready` |",
"| Integrate a managed thread | `heddle land` | `heddle land` |",
"| Synchronize source | `heddle pull` / `heddle push`",
"| Configure remotes | `heddle remote` | `heddle remote` |",
] {
assert!(
help.contains(mapping),
"git-concepts topic missing current mapping `{mapping}`: {help}"
);
}
assert!(
help.contains("Use `heddle init` to add that sidecar")
&& help.contains("`heddle adopt`")
&& help.contains("one atomic transition")
&& help.contains("makes Heddle the repository authority"),
"git-concepts topic should distinguish sidecar initialization from adoption: {help}"
);
assert!(
help.contains("optional Git-compatible client")
&& help.contains("it is not a Heddle dependency")
&& help.contains("`bridge git import`")
&& help.contains("`bridge git export`")
&& help.contains("`sync git`")
&& help.contains("translate data between authorities"),
"git-concepts topic should explain unsupported Git operations and authority translation: {help}"
);
for removed in ["heddle checkpoint", "heddle fetch", "heddle switch"] {
assert!(
!help.contains(removed),
"git-concepts topic must not advertise removed command `{removed}`: {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}"
);
}