use pretty_assertions::assert_eq;
use super::*;
use crate::agent::{
AgentDefinition, AgentId, AgentOrigin, AgentRuntimeSpec, ClaudeAgentConfig, ClaudeToolPolicy,
ModelPolicy, ModelSelection, PromptPolicy, ToolPolicy,
};
use crate::tui::line_editor::LineEditor;
use crate::tui::text_input::{AgentField, TextInput};
fn rho_draft() -> AgentDefinition {
AgentDefinition {
id: AgentId::new("draft").unwrap(),
description: "draft agent".into(),
prompt: PromptPolicy::Extend("body".into()),
runtime: AgentRuntimeSpec::Rho {
tools: ToolPolicy::All,
model: ModelPolicy::Inherit,
reasoning: None,
},
}
}
fn claude_draft() -> AgentDefinition {
AgentDefinition {
id: AgentId::new("claude-draft").unwrap(),
description: "claude draft".into(),
prompt: PromptPolicy::Extend("body".into()),
runtime: AgentRuntimeSpec::ClaudeCli(ClaudeAgentConfig {
tools: ClaudeToolPolicy::None,
inherit_claude_config: false,
model: None,
reasoning: None,
}),
}
}
fn field_values(picker: &UiPicker) -> Vec<&str> {
picker
.items
.iter()
.map(|item| item.value.as_str())
.collect()
}
#[test]
fn rho_field_picker_lists_runtime_specific_fields() {
let picker = agent_field_picker(&rho_draft());
let values = field_values(&picker);
assert!(values.contains(&AGENT_FIELD_MODEL_POLICY));
assert!(values.contains(&AGENT_FIELD_REASONING));
assert!(values.contains(&AGENT_FIELD_TOOLS));
assert!(values.contains(&AGENT_FIELD_SAVE));
assert!(values.contains(&AGENT_FIELD_CANCEL));
assert!(!values.contains(&AGENT_FIELD_MODEL));
assert!(!values.contains(&AGENT_FIELD_PROVIDER));
let mut explicit = rho_draft();
if let AgentRuntimeSpec::Rho { model, .. } = &mut explicit.runtime {
*model = ModelPolicy::Select(ModelSelection {
provider: None,
model: "gpt-5.5".into(),
});
}
let explicit_picker = agent_field_picker(&explicit);
let explicit_values = field_values(&explicit_picker);
assert!(explicit_values.contains(&AGENT_FIELD_MODEL));
assert!(explicit_values.contains(&AGENT_FIELD_PROVIDER));
assert!(!values.contains(&AGENT_FIELD_INHERIT_CLAUDE_CONFIG));
}
#[test]
fn claude_field_picker_hides_rho_only_fields() {
let picker = agent_field_picker(&claude_draft());
let values = field_values(&picker);
assert!(values.contains(&AGENT_FIELD_MODEL));
assert!(values.contains(&AGENT_FIELD_INHERIT_CLAUDE_CONFIG));
assert!(values.contains(&AGENT_FIELD_TOOLS));
assert!(!values.contains(&AGENT_FIELD_MODEL_POLICY));
assert!(!values.contains(&AGENT_FIELD_PROVIDER));
}
#[test]
fn edit_session_restores_inactive_runtime_settings() {
let mut session = AgentEditSession::new(
rho_draft(),
"agent.md".into(),
AgentOrigin::RhoHome,
".".into(),
String::new(),
);
assert!(session.switch_runtime("claude-cli"));
session.with_draft_mut(|draft| {
if let AgentRuntimeSpec::ClaudeCli(config) = &mut draft.runtime {
config.inherit_claude_config = true;
config.tools = ClaudeToolPolicy::Allow(vec!["Read".into()]);
}
});
assert!(session.switch_runtime("rho"));
assert!(matches!(
session.draft().runtime,
AgentRuntimeSpec::Rho {
tools: ToolPolicy::All,
..
}
));
assert!(session.switch_runtime("claude-cli"));
match &session.draft().runtime {
AgentRuntimeSpec::ClaudeCli(config) => {
assert!(config.inherit_claude_config);
assert_eq!(config.tools, ClaudeToolPolicy::Allow(vec!["Read".into()]));
}
_ => panic!("expected claude runtime"),
}
}
#[test]
fn model_policy_choice_for_claude_offers_inherit_and_select_only() {
let picker = agent_choice_picker(AgentChoiceField::ModelPolicy, &claude_draft());
let labels: Vec<&str> = picker
.items
.iter()
.map(|item| item.label.as_str())
.collect();
assert_eq!(labels, ["inherit", "select"]);
let rho_picker = agent_choice_picker(AgentChoiceField::ModelPolicy, &rho_draft());
let rho_labels: Vec<&str> = rho_picker
.items
.iter()
.map(|item| item.label.as_str())
.collect();
assert_eq!(rho_labels, ["inherit", "prefer", "require", "select"]);
}
#[test]
fn claude_reasoning_picker_omits_off_and_minimal() {
let reasoning_picker = agent_choice_picker(AgentChoiceField::Reasoning, &claude_draft());
let labels: Vec<&str> = reasoning_picker
.items
.iter()
.map(|item| item.label.as_str())
.collect();
assert!(labels.contains(&"inherit"));
assert!(!labels.contains(&"off"));
assert!(!labels.contains(&"minimal"));
assert!(labels.contains(&"high"));
}
#[test]
fn agent_text_input_edits_at_character_cursor() {
let mut input = TextInput::agent_field(AgentField::Description, "hello");
input.editor.cursor = 2;
input.editor.insert_char('X');
assert_eq!(input.editor.value, "heXllo");
assert_eq!(input.editor.cursor, 3);
input.editor.backspace();
assert_eq!(input.editor.value, "hello");
input.editor.insert_text("ab");
assert_eq!(input.editor.value, "heabllo");
}
#[test]
fn agent_text_input_strips_line_breaks_from_paste() {
let mut editor = LineEditor::new("[read_file");
editor.insert_text(", shell]\nextra");
assert_eq!(editor.value, "[read_file, shell]extra");
}
#[test]
fn authorize_editable_path_accepts_rho_home_agent() {
let dir = tempfile::tempdir().unwrap();
let agents = dir.path().join(".agents/agents");
std::fs::create_dir_all(&agents).unwrap();
let path = agents.join("demo.md");
std::fs::write(&path, "---\ndescription: demo\n---\n").unwrap();
let root = authorize_editable_path(AgentOrigin::Project, &path, dir.path()).unwrap();
assert_eq!(root, agents);
}