use crate::cmd::{Cli, Command, Error, Outcome, advance, prelude};
use crate::prompt::dispatch::advance::cli::AdvanceHandoff;
use clap::{CommandFactory, Parser};
use std::path::PathBuf;
fn parse(args: &[&str]) -> Command {
Cli::try_parse_from(args).expect("parses").command
}
#[test]
fn new_parses_optional_path() {
assert!(matches!(parse(&["lernie", "new"]), Command::New(a) if a.path.is_none()));
let Command::New(a) = parse(&["lernie", "new", "/w"]) else {
panic!()
};
assert_eq!(a.path.unwrap(), PathBuf::from("/w"));
}
#[test]
fn config_parses_name_and_flags() {
let Command::Config(a) = parse(&[
"lernie", "config", "/ws", "strict", "--from", "src", "--orphan",
]) else {
panic!()
};
assert_eq!(a.workspace, PathBuf::from("/ws"));
assert_eq!(a.name.unwrap(), "strict");
assert_eq!(a.from.unwrap(), "src");
assert!(a.orphan);
let Command::Config(b) = parse(&["lernie", "config", "/ws"]) else {
panic!()
};
assert!(b.name.is_none() && b.from.is_none() && !b.orphan);
}
#[test]
fn prompt_parses_repo_and_message() {
let Command::Prompt(a) = parse(&["lernie", "prompt", "/r", "hello there"]) else {
panic!()
};
assert_eq!(a.repo, PathBuf::from("/r"));
assert_eq!(a.message, "hello there");
}
#[test]
fn dispatch_parses_positional_and_goal() {
let Command::Dispatch(a) = parse(&[
"lernie", "dispatch", "worker", "/r", "br", "--goal", "do it",
]) else {
panic!()
};
assert_eq!((a.role.as_str(), a.branch.as_str()), ("worker", "br"));
assert_eq!(a.goal.unwrap(), "do it");
let Command::Dispatch(b) = parse(&["lernie", "dispatch", "compactor", "/r", "br"]) else {
panic!()
};
assert!(b.goal.is_none());
}
#[test]
fn stop_parses_stop_children_flag() {
let Command::Stop(a) = parse(&["lernie", "stop", "/r", "br", "--stop-children"]) else {
panic!()
};
assert!(a.stop_children);
let Command::Stop(b) = parse(&["lernie", "stop", "/r", "br"]) else {
panic!()
};
assert!(!b.stop_children);
}
#[test]
fn message_scan_bundle_replay_advance_parse() {
let Command::Message(m) = parse(&["lernie", "message", "/ws", "ag", "hi"]) else {
panic!()
};
assert_eq!((m.agent.as_str(), m.content.as_str()), ("ag", "hi"));
let Command::Scan(s) = parse(&["lernie", "scan", "/ws"]) else {
panic!()
};
assert_eq!(s.workspace, PathBuf::from("/ws"));
let Command::Bundle(b) = parse(&["lernie", "bundle", "/ws", "ag", "/out"]) else {
panic!()
};
assert_eq!(b.out_dir, PathBuf::from("/out"));
let Command::Replay(rep) = parse(&["lernie", "replay", "/a"]) else {
panic!()
};
assert_eq!(rep.archive, PathBuf::from("/a"));
let Command::Advance(v) = parse(&["lernie", "advance", "/ws", "ag"]) else {
panic!()
};
assert_eq!(
(v.workspace, v.agent.as_str()),
(PathBuf::from("/ws"), "ag")
);
}
#[test]
fn tool_and_prime_parse() {
assert!(matches!(parse(&["lernie", "tool", "bash"]), Command::Tool(t) if t.name == "bash"));
assert!(matches!(parse(&["lernie", "prime"]), Command::Prime(_)));
}
#[test]
fn error_display_is_the_prefixed_stderr_shape() {
assert_eq!(Error::new("new", "boom").to_string(), "lernie new: boom");
assert_eq!(
Error::new(format!("dispatch {}", "worker"), "bad role").to_string(),
"lernie dispatch worker: bad role"
);
assert_eq!(
Error::new(format!("tool {}", "bash"), "no such").to_string(),
"lernie tool bash: no such"
);
assert!(format!("{:?}", Error::new("scan", "x")).contains("scan"));
}
#[test]
fn advance_outcome_maps_both_handoff_arms() {
let exec = advance::outcome_of(AdvanceHandoff::Exec(std::process::Command::new("true")));
assert!(matches!(exec, Outcome::Exec(_)));
let done = advance::outcome_of(AdvanceHandoff::Done);
assert!(matches!(done, Outcome::Quiet));
}
#[test]
fn noop_editor_is_a_silent_ok() {
super::noop_editor(std::path::Path::new("/unused")).unwrap();
}
#[test]
fn prelude_reexports_the_binding_mechanisms() {
let _leader: fn() = prelude::become_pgid_leader;
let _handler: fn() = prelude::install_stop_handler;
let flag: &std::sync::atomic::AtomicBool = prelude::stop_flag();
let _ = flag.load(std::sync::atomic::Ordering::SeqCst);
}
#[test]
fn cli_version_pairs_lernie_and_the_brazen_pin() {
let v = crate::cmd::cli_version();
assert!(v.starts_with(env!("CARGO_PKG_VERSION")), "{v}");
let brazen = v
.strip_prefix(env!("CARGO_PKG_VERSION"))
.and_then(|rest| rest.strip_prefix(" (brazen "))
.and_then(|rest| rest.strip_suffix(')'))
.unwrap_or_else(|| panic!("unexpected cli_version shape: {v}"));
assert_eq!(brazen, crate::prompt::brazen_pin());
assert_eq!(Cli::command().get_version(), Some(v));
}
#[test]
fn every_positional_argument_documents_itself() {
let cli = Cli::command();
for sub in cli.get_subcommands() {
for arg in sub.get_positionals() {
assert!(
arg.get_help().is_some(),
"{} <{}> renders blank in --help",
sub.get_name(),
arg.get_id()
);
}
}
}
#[test]
fn tool_name_help_names_the_built_in_pool() {
let cli = Cli::command();
let tool = cli
.get_subcommands()
.find(|s| s.get_name() == "tool")
.expect("tool verb");
let arg = tool.get_positionals().next().expect("<NAME> positional");
let help = arg.get_help().expect("help").to_string();
for name in crate::prompt::tool::builtin::NAMES {
assert!(help.contains(name), "{help}");
}
assert!(!help.contains("write_summary"), "{help}");
}
#[test]
fn preludes_are_named_per_verb_by_the_surface() {
let want =
|c: Command| -> Vec<*const ()> { c.preludes().iter().map(|f| *f as *const ()).collect() };
let leader = prelude::become_pgid_leader as *const ();
let handler = prelude::install_stop_handler as *const ();
for argv in [
&["lernie", "prompt", "/w", "hi"][..],
&["lernie", "advance", "/w", "20260101-a1"][..],
] {
assert_eq!(want(parse(argv)), vec![leader, handler], "{argv:?}");
}
assert_eq!(
want(parse(&[
"lernie", "dispatch", "worker", "/w", "b", "--goal", "g"
])),
vec![leader],
);
for argv in [
&["lernie", "new"][..],
&["lernie", "config", "/w"][..],
&["lernie", "stop", "/w", "20260101-a1"][..],
&["lernie", "message", "/w", "20260101-a1", "c"][..],
&["lernie", "scan", "/w"][..],
&["lernie", "bundle", "/w", "20260101-a1", "/out"][..],
&["lernie", "replay", "/b.bundle"][..],
&["lernie", "tool", "bash"][..],
&["lernie", "prime"][..],
] {
assert!(parse(argv).preludes().is_empty(), "{argv:?}");
}
}