#![allow(clippy::missing_docs_in_private_items)]
use super::*;
fn unique_dir(tag: &str) -> std::path::PathBuf {
std::env::temp_dir().join(format!("moadim-agents-{tag}-{}", uuid::Uuid::new_v4()))
}
#[test]
fn available_agents_in_falls_back_when_dir_has_no_toml() {
let dir = unique_dir("empty-readable");
std::fs::create_dir_all(&dir).unwrap();
std::fs::write(dir.join("notes.txt"), "ignore me").unwrap();
assert_eq!(
available_agents_in(&dir),
vec![
"claude".to_string(),
"codex".to_string(),
"hermes".to_string()
]
);
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn load_agent_command_parses_a_valid_config() {
let agent_name = "load-agent-valid-zzz";
std::fs::create_dir_all(crate::paths::agents_dir()).unwrap();
let cfg = crate::paths::agent_toml_path(agent_name);
std::fs::write(&cfg, "command = \"claude\"\nargs = [\"--help\"]\n").unwrap();
let loaded = load_agent_command(agent_name).unwrap();
assert_eq!(loaded.command, "claude");
assert_eq!(loaded.args, vec!["--help".to_string()]);
std::fs::remove_file(&cfg).unwrap();
}
#[test]
fn load_agent_command_reports_parse_error_for_malformed_config() {
let agent_name = "load-agent-malformed-zzz";
std::fs::create_dir_all(crate::paths::agents_dir()).unwrap();
let cfg = crate::paths::agent_toml_path(agent_name);
std::fs::write(&cfg, "command = [\n").unwrap();
match load_agent_command(agent_name) {
Err(AgentLoadError::Parse(err)) => assert!(!err.is_empty()),
other => panic!("expected Parse error, got {other:?}"),
}
std::fs::remove_file(&cfg).unwrap();
}
#[test]
fn load_agent_command_reports_missing_for_absent_config() {
assert!(matches!(
load_agent_command("load-agent-absent-zzz"),
Err(AgentLoadError::Missing)
));
}
#[test]
fn load_agent_command_reports_unreadable_for_non_not_found_io_error() {
let agent_name = "load-agent-unreadable-zzz";
std::fs::create_dir_all(crate::paths::agents_dir()).unwrap();
let cfg = crate::paths::agent_toml_path(agent_name);
std::fs::create_dir_all(&cfg).unwrap();
match load_agent_command(agent_name) {
Err(AgentLoadError::Unreadable(err)) => assert!(!err.is_empty()),
other => panic!("expected Unreadable error, got {other:?}"),
}
std::fs::remove_dir_all(&cfg).unwrap();
}
#[test]
fn agent_load_error_display_distinguishes_variants() {
assert_eq!(
AgentLoadError::Missing.to_string(),
"agent config not found"
);
assert_eq!(
AgentLoadError::Unreadable("permission denied".to_string()).to_string(),
"unreadable agent config: permission denied"
);
assert_eq!(
AgentLoadError::Parse("boom".to_string()).to_string(),
"malformed agent TOML: boom"
);
}
#[test]
fn ensure_default_agents_seeds_into_override_home() {
let home = unique_dir("ensure-default");
let previous = std::env::var_os("MOADIM_HOME_OVERRIDE");
unsafe {
std::env::set_var("MOADIM_HOME_OVERRIDE", &home);
}
ensure_default_agents();
assert!(crate::paths::agents_dir().join("claude.toml").exists());
unsafe {
match previous {
Some(value) => std::env::set_var("MOADIM_HOME_OVERRIDE", value),
None => std::env::remove_var("MOADIM_HOME_OVERRIDE"),
}
}
let _ = std::fs::remove_dir_all(&home);
}
#[test]
fn ensure_default_agents_in_returns_early_when_dir_is_uncreatable() {
let base = unique_dir("uncreatable");
std::fs::create_dir_all(&base).unwrap();
let file = base.join("iamafile");
std::fs::write(&file, "x").unwrap();
let unmakeable = file.join("sub");
ensure_default_agents_in(&unmakeable);
assert!(!unmakeable.exists());
let _ = std::fs::remove_dir_all(&base);
}
#[test]
fn ensure_default_agents_in_logs_and_continues_on_write_failure() {
let dir = unique_dir("write-fail");
std::fs::create_dir_all(&dir).unwrap();
std::fs::create_dir_all(dir.join("claude.toml")).unwrap();
ensure_default_agents_in(&dir);
assert!(dir.join("claude.toml").is_dir());
assert!(dir.join("codex.toml").is_file());
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn builtin_configs_declare_expected_instructions_file() {
let claude: AgentCommand = toml::from_str(claude_code::CONFIG).unwrap();
assert_eq!(claude.instructions_file, "AGENTS.md");
let codex: AgentCommand = toml::from_str(codex::CONFIG).unwrap();
assert_eq!(codex.instructions_file, "AGENTS.md");
}
#[test]
fn default_instructions_file_falls_back_to_claude_md() {
let agent: AgentCommand = toml::from_str(r#"command = "x""#).unwrap();
assert_eq!(agent.instructions_file, DEFAULT_INSTRUCTIONS_FILE);
assert_eq!(agent.instructions_file, "CLAUDE.md");
}
#[cfg(unix)]
#[test]
fn ensure_default_agents_in_swallows_per_config_write_errors() {
use std::os::unix::fs::PermissionsExt as _;
let dir = unique_dir("write-fail");
std::fs::create_dir_all(&dir).unwrap();
std::fs::set_permissions(&dir, std::fs::Permissions::from_mode(0o555)).unwrap();
ensure_default_agents_in(&dir);
std::fs::set_permissions(&dir, std::fs::Permissions::from_mode(0o755)).unwrap();
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn available_agents_in_ignores_files_without_extension() {
let dir = unique_dir("no-ext");
std::fs::create_dir_all(&dir).unwrap();
std::fs::write(dir.join("readme"), "not an agent").unwrap();
std::fs::write(dir.join("my-agent.toml"), "command = \"x\"\n").unwrap();
let agents = available_agents_in(&dir);
assert!(
agents.contains(&"my-agent".to_string()),
"toml file should be listed"
);
assert!(
!agents.iter().any(|n| n == "readme"),
"no-extension file must be filtered out"
);
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn available_agents_in_ignores_files_with_non_toml_extension() {
let dir = unique_dir("non-toml-ext");
std::fs::create_dir_all(&dir).unwrap();
std::fs::write(dir.join("notes.txt"), "ignore").unwrap();
std::fs::write(dir.join("claude.toml"), "command = \"claude\"\n").unwrap();
let agents = available_agents_in(&dir);
assert_eq!(agents, vec!["claude".to_string()]);
let _ = std::fs::remove_dir_all(&dir);
}