Files

119 lines
4.1 KiB
Rust

//! Environment profiles (issue #40).
mod common;
use agent_manager::cli::{Cli, Command, ProfileCmd, StartArgs};
use agent_manager::commands::{profile_cmd, run_cmd};
use clap::Parser;
fn app_with_profiles(tag: &str) -> agent_manager::app::App {
let dir = std::env::temp_dir().join(format!("am-profiles-{tag}-{}", std::process::id()));
std::fs::create_dir_all(&dir).unwrap();
let cfg = dir.join("config.yaml");
std::fs::write(
&cfg,
"version: \"1.0\"\nsettings:\n auto_install_deps: false\n confirm_before_run: false\nprofiles:\n dev:\n agent: claude-code\n env:\n DEBUG: \"1\"\n LOG_LEVEL: debug\n args: [--model, sonnet]\n prod:\n env:\n LOG_LEVEL: info\nagents:\n - name: claude-code\n run: claude\n install: {type: npm, package: \"@anthropic-ai/claude-code\"}\n",
)
.unwrap();
let cli = Cli::parse_from(["am", "--config", cfg.to_str().unwrap()]);
let mut app = agent_manager::app::App::from_cli(cli).unwrap();
let mut p = app.paths.clone();
p.install_dir = dir.join("agents");
p.bin_dir = dir.join("agents").join("bin");
p.log_dir = dir.join("logs");
p.state_file = dir.join("state.json");
p.probe_cache_file = dir.join("probe-cache.json");
p.config_dir = Some(dir.join("config"));
app.paths = p;
app
}
#[test]
fn profile_list_and_show() {
let app = app_with_profiles("list");
assert_eq!(profile_cmd::run_list(&app).unwrap(), 0);
assert_eq!(profile_cmd::run_show(&app, "dev").unwrap(), 0);
let json = app; // keep clippy quiet about unused paths
let _ = json;
}
#[test]
fn unknown_profile_lists_available_ones() {
let app = app_with_profiles("unknown");
let err = profile_cmd::resolve(&app, "staging").unwrap_err().to_string();
assert!(err.contains("unknown profile 'staging'"), "{err}");
assert!(err.contains("dev") && err.contains("prod"), "{err}");
}
#[test]
fn apply_merges_profile_env_and_args() {
let app = app_with_profiles("apply");
let (args, env) = profile_cmd::apply(
&app,
Some("prod"),
&["--base".to_string()],
&std::collections::BTreeMap::from([("KEEP".to_string(), "1".to_string())]),
)
.unwrap();
assert_eq!(args, vec!["--base"]);
assert_eq!(env.get("LOG_LEVEL").map(String::as_str), Some("info"));
assert_eq!(env.get("KEEP").map(String::as_str), Some("1"));
}
#[test]
fn start_with_unknown_profile_fails_cleanly() {
let app = app_with_profiles("badstart");
let opts = StartArgs {
agent: Some("claude-code".to_string()),
profile: Some("staging".to_string()),
..Default::default()
};
let err = run_cmd::start(&app, &opts).unwrap_err().to_string();
assert!(err.contains("unknown profile"), "{err}");
assert!(err.contains("dev"), "{err}");
}
#[test]
fn start_with_profile_dry_run_succeeds() {
let mut app = app_with_profiles("dry");
// Force dry-run so start_one never spawns.
app.cli.dry_run = true;
let opts = StartArgs {
agent: Some("claude-code".to_string()),
profile: Some("dev".to_string()),
..Default::default()
};
assert_eq!(run_cmd::start(&app, &opts).unwrap(), 0);
}
#[test]
fn start_without_agent_uses_profile_default_agent() {
let mut app = app_with_profiles("default");
app.cli.dry_run = true;
let opts = StartArgs {
agent: None,
profile: Some("dev".to_string()),
..Default::default()
};
assert_eq!(run_cmd::start(&app, &opts).unwrap(), 0);
}
#[test]
fn cli_parses_profile_subcommands() {
let cli = Cli::parse_from(["am", "profile", "list"]);
assert!(matches!(
cli.command,
Some(Command::Profile(ProfileCmd::List))
));
let cli = Cli::parse_from(["am", "profile", "show", "dev"]);
assert!(matches!(
cli.command,
Some(Command::Profile(ProfileCmd::Show { ref name })) if name == "dev"
));
let cli = Cli::parse_from(["am", "start", "claude-code", "--profile", "dev"]);
match cli.command {
Some(Command::Start(a)) => assert_eq!(a.profile.as_deref(), Some("dev")),
other => panic!("unexpected: {other:?}"),
}
}