v0.4.1 : Phase 1 complete (17/17) - dashboard TUI (#33), favoris/notes/tags (#39), profils d'environnement (#40), completions dynamiques + man pages (#41), packaging officiel + CI (#42), self-update Gitea, state v3, 212 tests
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
//! Dynamic completions + man pages (issue #41).
|
||||
|
||||
mod common;
|
||||
|
||||
use agent_manager::cli::{Cli, Command};
|
||||
use agent_manager::commands::{agents_cmd, completion_cmd, man_cmd};
|
||||
use agent_manager::state::InstalledEntry;
|
||||
use clap::CommandFactory;
|
||||
use clap::Parser;
|
||||
use clap_complete::Shell;
|
||||
|
||||
fn generate(shell: Shell, installed: bool) -> String {
|
||||
let mut cmd = Cli::command();
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
clap_complete::generate(shell, &mut cmd, "am", &mut buf);
|
||||
if installed {
|
||||
buf.push(b'\n');
|
||||
}
|
||||
let base = String::from_utf8(buf).unwrap();
|
||||
if !installed {
|
||||
return base;
|
||||
}
|
||||
// Reuse the command's own generation path (with the dynamic layer).
|
||||
// completion_cmd writes to stdout; emulate by calling its logic through
|
||||
// the shell snippet functions instead.
|
||||
let _ = base;
|
||||
match shell {
|
||||
Shell::Bash => format!("{}
|
||||
{}", base, completion_cmd::bash_snippet()),
|
||||
Shell::Zsh => format!("{}
|
||||
{}", base, completion_cmd::zsh_snippet()),
|
||||
Shell::Fish => format!("{}
|
||||
{}", base, completion_cmd::fish_snippet()),
|
||||
Shell::PowerShell => {
|
||||
let marker = "Register-ArgumentCompleter -Native -CommandName 'am' -ScriptBlock {";
|
||||
let mut text = base.replace(marker, "$amBase = {");
|
||||
text.push('\n');
|
||||
text.push_str(completion_cmd::powershell_snippet());
|
||||
text
|
||||
}
|
||||
Shell::Elvish => format!("{}
|
||||
{}", base, completion_cmd::elvish_snippet()),
|
||||
_ => base,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn base_script_has_no_dynamic_layer() {
|
||||
for shell in [Shell::Bash, Shell::Zsh, Shell::Fish, Shell::PowerShell, Shell::Elvish] {
|
||||
let script = generate(shell, false);
|
||||
assert!(!script.contains("_agents --installed"), "{shell:?}");
|
||||
assert!(!script.contains("_am_installed"), "{shell:?}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn installed_mode_embeds_the_dynamic_layer() {
|
||||
for shell in [Shell::Bash, Shell::Zsh, Shell::Fish, Shell::PowerShell, Shell::Elvish] {
|
||||
let script = generate(shell, true);
|
||||
assert!(script.contains("_agents --installed"), "{shell:?}");
|
||||
assert!(script.contains("favorite"), "{shell:?}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn agents_command_lists_installed_only_in_installed_mode() {
|
||||
let app = common::test_app("agents", &["list"]);
|
||||
let entry = InstalledEntry {
|
||||
name: "claude-code".to_string(),
|
||||
version: Some("1.0.0".to_string()),
|
||||
method: "npm".to_string(),
|
||||
install_dir: "/tmp/cc".to_string(),
|
||||
bins: vec![],
|
||||
run: "claude".to_string(),
|
||||
installed_at: "2026-08-01T00:00:00Z".to_string(),
|
||||
updated_at: None,
|
||||
pid: None,
|
||||
started_at: None,
|
||||
};
|
||||
app.state.set(&entry).unwrap();
|
||||
let names = agents_cmd::agent_names(&app, true);
|
||||
assert!(names.contains(&"claude-code".to_string()));
|
||||
assert!(!names.contains(&"ante".to_string()), "an agent that was never installed must not appear: {names:?}");
|
||||
let all = agents_cmd::agent_names(&app, false);
|
||||
assert!(all.contains(&"ante".to_string()), "default mode keeps the whole catalog");
|
||||
assert_eq!(agents_cmd::run(&app, true).unwrap(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn man_pages_are_written_to_output_dir() {
|
||||
let app = common::test_app("man", &["man"]);
|
||||
let dir = std::env::temp_dir().join(format!("am-man-{}", std::process::id()));
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
let code = man_cmd::run(&app, None, Some(&dir)).unwrap();
|
||||
assert_eq!(code, 0);
|
||||
assert!(dir.join("am.1").exists(), "am.1 missing");
|
||||
for name in ["start", "sessions", "stats", "log", "history", "dashboard", "favorite", "profile", "man"] {
|
||||
assert!(
|
||||
dir.join(format!("am-{name}.1")).exists(),
|
||||
"am-{name}.1 missing"
|
||||
);
|
||||
}
|
||||
let text = std::fs::read_to_string(dir.join("am.1")).unwrap();
|
||||
assert!(text.contains(".TH"), "a roff page must start with .TH: {text}");
|
||||
assert!(text.contains("dashboard"), "new commands must appear in the page");
|
||||
let code = man_cmd::run(&app, Some("install"), Some(&dir)).unwrap();
|
||||
assert_eq!(code, 0);
|
||||
assert!(dir.join("am-install.1").exists());
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_man_command_is_rejected() {
|
||||
let app = common::test_app("manbad", &["man"]);
|
||||
let err = man_cmd::run(&app, Some("does-not-exist"), None).unwrap_err().to_string();
|
||||
assert!(err.contains("unknown command"), "{err}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cli_parses_completion_installed_flag() {
|
||||
let cli = Cli::parse_from(["am", "completion", "bash", "--installed"]);
|
||||
match cli.command {
|
||||
Some(Command::Completion { installed, .. }) => assert!(installed),
|
||||
other => panic!("unexpected: {other:?}"),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user