Files
galaxy/crates/galaxy_cli/src/lib_tests.rs
T

2432 lines
67 KiB
Rust

use std::ffi::OsString;
use clap::Parser;
use super::*;
use crate::agent::{AgentCommand, Harness, OutputFormat};
use crate::artifact::ArtifactCommand;
use crate::environment::{EnvironmentCommand, ImageCommand};
use crate::harness_support::{HarnessSupportCommand, TaskStatus};
use crate::integration::IntegrationCommand;
use crate::memory_store::{MemoryCommand, MemoryStoreCommand};
use crate::schedule::ScheduleSubcommand;
use crate::secret::{CodexMethod, CreateProvider, SecretCommand};
use crate::task::{MessageCommand, TaskCommand};
#[test]
fn identifies_worker_subcommands() {
assert!(is_worker_invocation("minidump-server"));
#[cfg(unix)]
assert!(is_worker_invocation(&terminal_server_subcommand()));
#[cfg(feature = "plugin_host")]
assert!(is_worker_invocation("--plugin-host"));
assert!(!is_worker_invocation("--prompt"));
}
fn set_env_var(name: &str, value: &str) -> Option<OsString> {
let previous = std::env::var_os(name);
// Safety: tests that mutate process environment are marked `serial` so we
// do not race with other environment readers/writers in this crate.
unsafe { std::env::set_var(name, value) };
previous
}
fn restore_env_var(name: &str, previous: Option<OsString>) {
match previous {
// Safety: tests that mutate process environment are marked `serial` so
// we do not race with other environment readers/writers in this crate.
Some(value) => unsafe { std::env::set_var(name, value) },
// Safety: tests that mutate process environment are marked `serial` so
// we do not race with other environment readers/writers in this crate.
None => unsafe { std::env::remove_var(name) },
}
}
#[test]
fn agent_run_accepts_model() {
let args = Args::try_parse_from([
"warp", "agent", "run", "--prompt", "hello", "--model", "gpt-4o",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp agent run` command");
};
let CliCommand::Agent(AgentCommand::Run(run_args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp agent run` command");
};
assert_eq!(run_args.model.model.as_deref(), Some("gpt-4o"));
}
#[test]
fn agent_run_accepts_hidden_bedrock_inference_role_flag() {
let args = Args::try_parse_from([
"warp",
"agent",
"run",
"--prompt",
"hello",
"--bedrock-inference-role",
"arn:aws:iam::123456789012:role/test",
"--bedrock-role-region",
"us-east-1",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp agent run` command");
};
let CliCommand::Agent(AgentCommand::Run(run_args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp agent run` command");
};
assert_eq!(
run_args.bedrock_inference_role.as_deref(),
Some("arn:aws:iam::123456789012:role/test")
);
assert_eq!(run_args.bedrock_role_region.as_deref(), Some("us-east-1"));
}
#[test]
fn agent_run_rejects_bedrock_inference_role_without_region() {
let err = Args::try_parse_from([
"warp",
"agent",
"run",
"--prompt",
"hello",
"--bedrock-inference-role",
"arn:aws:iam::123456789012:role/test",
])
.expect_err("--bedrock-inference-role must require --bedrock-role-region");
assert!(
err.to_string().contains("--bedrock-role-region"),
"expected error to reference --bedrock-role-region, got: {err}"
);
}
#[test]
fn agent_run_rejects_bedrock_role_region_without_role() {
let err = Args::try_parse_from([
"warp",
"agent",
"run",
"--prompt",
"hello",
"--bedrock-role-region",
"us-east-1",
])
.expect_err("--bedrock-role-region must require --bedrock-inference-role");
assert!(
err.to_string().contains("--bedrock-inference-role"),
"expected error to reference --bedrock-inference-role, got: {err}"
);
}
#[test]
fn model_list_parses() {
let args = Args::try_parse_from(["warp", "model", "list"]).unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp model list` command");
};
let CliCommand::Model(model_cmd) = boxed_cmd.as_ref() else {
panic!("Expected `warp model` command");
};
assert!(matches!(model_cmd, crate::model::ModelCommand::List));
}
#[test]
fn memory_store_list_parses() {
let args = Args::try_parse_from(["warp", "memory-store", "list"]).unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp memory-store list` command");
};
let CliCommand::MemoryStore(memory_store_cmd) = boxed_cmd.as_ref() else {
panic!("Expected `warp memory-store` command");
};
assert!(matches!(memory_store_cmd, MemoryStoreCommand::List));
}
#[test]
fn memory_stores_alias_parses() {
let args = Args::try_parse_from(["warp", "memory-stores", "list"]).unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp memory-stores list` command");
};
let CliCommand::MemoryStore(memory_store_cmd) = boxed_cmd.as_ref() else {
panic!("Expected `warp memory-stores` alias to parse as memory-store command");
};
assert!(matches!(memory_store_cmd, MemoryStoreCommand::List));
}
#[test]
fn memory_list_parses() {
let args = Args::try_parse_from(["warp", "memory", "list", "store-123"]).unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp memory list` command");
};
let CliCommand::Memory(MemoryCommand::List(args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp memory list` command");
};
assert_eq!(args.store_uid, "store-123");
}
#[test]
fn memory_store_get_parses() {
let args = Args::try_parse_from(["warp", "memory-store", "get", "store-123"]).unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp memory-store get` command");
};
let CliCommand::MemoryStore(MemoryStoreCommand::Get(args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp memory-store get` command");
};
assert_eq!(args.store_uid, "store-123");
}
#[test]
fn memory_store_get_store_alias_parses() {
let args = Args::try_parse_from(["warp", "memory-store", "get-store", "store-123"]).unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp memory-store get-store` command");
};
let CliCommand::MemoryStore(MemoryStoreCommand::Get(args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp memory-store get-store` alias to parse as get command");
};
assert_eq!(args.store_uid, "store-123");
}
#[test]
fn memory_store_update_parses() {
let args = Args::try_parse_from([
"warp",
"memory-store",
"update",
"store-123",
"--description",
"team memory store",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp memory-store update` command");
};
let CliCommand::MemoryStore(MemoryStoreCommand::Update(args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp memory-store update` command");
};
assert_eq!(args.store_uid, "store-123");
assert_eq!(args.description.as_deref(), Some("team memory store"));
}
#[test]
fn memory_store_update_store_alias_parses() {
let args = Args::try_parse_from([
"warp",
"memory-store",
"update-store",
"store-123",
"--description",
"team memory store",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp memory-store update-store` command");
};
let CliCommand::MemoryStore(MemoryStoreCommand::Update(args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp memory-store update-store` alias to parse as update command");
};
assert_eq!(args.store_uid, "store-123");
assert_eq!(args.description.as_deref(), Some("team memory store"));
}
#[test]
fn memory_create_parses() {
let args = Args::try_parse_from([
"warp",
"memory",
"create",
"store-123",
"--content",
"remember this",
"--reason",
"manual note",
"--version",
"v1",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp memory create` command");
};
let CliCommand::Memory(MemoryCommand::Create(args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp memory create` command");
};
assert_eq!(args.store_uid, "store-123");
assert_eq!(args.content, "remember this");
assert_eq!(args.reason, "manual note");
assert_eq!(args.version.as_deref(), Some("v1"));
}
#[test]
fn memory_update_parses() {
let args = Args::try_parse_from([
"warp",
"memory",
"update",
"memory-123",
"--store",
"store-123",
"--content",
"updated memory",
"--reason",
"manual edit",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp memory update` command");
};
let CliCommand::Memory(MemoryCommand::Update(args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp memory update` command");
};
assert_eq!(args.memory_uid, "memory-123");
assert_eq!(args.store_uid, "store-123");
assert_eq!(args.content, "updated memory");
assert_eq!(args.reason, "manual edit");
}
#[test]
fn memory_delete_parses() {
let args = Args::try_parse_from([
"warp",
"memory",
"delete",
"memory-123",
"--store",
"store-123",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp memory delete` command");
};
let CliCommand::Memory(MemoryCommand::Delete(args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp memory delete` command");
};
assert_eq!(args.memory_uid, "memory-123");
assert_eq!(args.store_uid, "store-123");
}
#[test]
fn memory_versions_parses() {
let args = Args::try_parse_from([
"warp",
"memory",
"versions",
"memory-123",
"--store",
"store-123",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp memory versions` command");
};
let CliCommand::Memory(MemoryCommand::Versions(args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp memory versions` command");
};
assert_eq!(args.memory_uid, "memory-123");
assert_eq!(args.store_uid, "store-123");
}
#[test]
fn legacy_memory_store_memory_commands_are_rejected() {
for command in [
"list-memories",
"memories",
"create-memory",
"add-memory",
"update-memory",
"edit-memory",
"delete-memory",
"remove-memory",
"list-versions",
"versions",
] {
let err = Args::try_parse_from(["warp", "memory-store", command, "memory-123"])
.expect_err("legacy memory-store memory command should not parse");
assert_eq!(err.kind(), clap::error::ErrorKind::InvalidSubcommand);
}
}
#[test]
fn login_parses() {
let args = Args::try_parse_from(["warp", "login"]).unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp login` command");
};
assert!(matches!(boxed_cmd.as_ref(), CliCommand::Login));
}
#[test]
fn logout_parses() {
let args = Args::try_parse_from(["warp", "logout"]).unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp logout` command");
};
assert!(matches!(boxed_cmd.as_ref(), CliCommand::Logout));
}
#[test]
fn agent_run_accepts_file() {
let args = Args::try_parse_from([
"warp",
"agent",
"run",
"--prompt",
"hello",
"--file",
"config.yaml",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp agent run` command");
};
let CliCommand::Agent(AgentCommand::Run(run_args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp agent run` command");
};
assert_eq!(
run_args.config_file.file.as_ref().and_then(|p| p.to_str()),
Some("config.yaml")
);
}
#[test]
fn agent_run_accepts_idle_on_complete_flag() {
let args = Args::try_parse_from([
"warp",
"agent",
"run",
"--prompt",
"hello",
"--idle-on-complete",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp agent run` command");
};
let CliCommand::Agent(AgentCommand::Run(run_args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp agent run` command");
};
assert_eq!(
run_args.idle_on_complete,
Some(humantime::Duration::from(std::time::Duration::from_secs(
45 * 60
)))
);
}
#[test]
fn agent_run_accepts_idle_on_complete_duration() {
let args = Args::try_parse_from([
"warp",
"agent",
"run",
"--prompt",
"hello",
"--idle-on-complete",
"10m",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp agent run` command");
};
let CliCommand::Agent(AgentCommand::Run(run_args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp agent run` command");
};
assert_eq!(
run_args.idle_on_complete,
Some(humantime::Duration::from(std::time::Duration::from_secs(
10 * 60
)))
);
}
#[test]
fn agent_run_accepts_skip_initial_turn_with_task_id_and_idle_on_complete() {
let args = Args::try_parse_from([
"warp",
"agent",
"run",
"--task-id",
"abc",
"--skip-initial-turn",
"--idle-on-complete",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp agent run` command");
};
let CliCommand::Agent(AgentCommand::Run(run_args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp agent run` command");
};
assert_eq!(run_args.task_id.as_deref(), Some("abc"));
assert!(run_args.skip_initial_turn);
assert!(run_args.idle_on_complete.is_some());
}
#[test]
fn agent_run_rejects_skip_initial_turn_without_idle_on_complete() {
// Without `--idle-on-complete`, the driver would exit immediately on Success
// before any follow-up could arrive, defeating the purpose of skip. Pinned at
// the CLI layer so the invariant fails loudly at parse time instead of at
// runtime.
let result = Args::try_parse_from([
"warp",
"agent",
"run",
"--task-id",
"abc",
"--skip-initial-turn",
]);
assert!(
result.is_err(),
"--skip-initial-turn without --idle-on-complete should fail to parse"
);
}
#[test]
fn agent_run_rejects_skip_initial_turn_without_task_id() {
// `--skip-initial-turn` is only meaningful on the server-side prompt path,
// which requires `--task-id`.
let result = Args::try_parse_from([
"warp",
"agent",
"run",
"--prompt",
"hello",
"--skip-initial-turn",
"--idle-on-complete",
]);
assert!(
result.is_err(),
"--skip-initial-turn without --task-id should fail to parse"
);
}
#[test]
fn agent_run_accepts_snapshot_flags() {
let args = Args::try_parse_from([
"warp",
"agent",
"run",
"--prompt",
"hello",
"--no-snapshot",
"--snapshot-upload-timeout",
"90s",
"--snapshot-script-timeout",
"45s",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp agent run` command");
};
let CliCommand::Agent(AgentCommand::Run(run_args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp agent run` command");
};
assert!(run_args.snapshot.no_snapshot);
assert_eq!(
run_args.snapshot.snapshot_upload_timeout,
Some(humantime::Duration::from(std::time::Duration::from_secs(
90
)))
);
assert_eq!(
run_args.snapshot.snapshot_script_timeout,
Some(humantime::Duration::from(std::time::Duration::from_secs(
45
)))
);
}
#[test]
fn agent_run_cloud_accepts_file_short_flag() {
let args = Args::try_parse_from([
"warp",
"agent",
"run-cloud",
"--prompt",
"hello",
"-f",
"config.json",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp agent run-cloud` command");
};
let CliCommand::Agent(AgentCommand::RunCloud(run_args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp agent run-cloud` command");
};
assert_eq!(
run_args.config_file.file.as_ref().and_then(|p| p.to_str()),
Some("config.json")
);
}
#[test]
fn agent_run_cloud_accepts_model() {
let args = Args::try_parse_from([
"warp",
"agent",
"run-cloud",
"--prompt",
"hello",
"--model",
"gpt-4o",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp agent run-cloud` command");
};
let CliCommand::Agent(AgentCommand::RunCloud(run_args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp agent run-cloud` command");
};
assert_eq!(run_args.model.model.as_deref(), Some("gpt-4o"));
}
#[test]
fn agent_run_cloud_accepts_agent_flag() {
let args = Args::try_parse_from([
"warp",
"agent",
"run-cloud",
"--prompt",
"hello",
"--agent",
"agent_123",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp agent run-cloud` command");
};
let CliCommand::Agent(AgentCommand::RunCloud(run_args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp agent run-cloud` command");
};
assert_eq!(run_args.agent_uid.as_deref(), Some("agent_123"));
}
#[test]
fn agent_run_cloud_accepts_mcp() {
let uuid = uuid::Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
let args = Args::try_parse_from([
"warp",
"agent",
"run-cloud",
"--prompt",
"hello",
"--mcp",
"550e8400-e29b-41d4-a716-446655440000",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp agent run-cloud` command");
};
let CliCommand::Agent(AgentCommand::RunCloud(run_args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp agent run-cloud` command");
};
assert!(matches!(
run_args.mcp_specs.as_slice(),
[crate::mcp::MCPSpec::Uuid(parsed_uuid)] if *parsed_uuid == uuid
));
}
#[test]
fn agent_run_cloud_accepts_run_ambient_alias() {
// Ensure backwards compatibility: run-ambient should still work as an alias
let args = Args::try_parse_from(["warp", "agent", "run-ambient", "--prompt", "hello"]).unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp agent run-ambient` (alias) command");
};
let CliCommand::Agent(AgentCommand::RunCloud(_)) = boxed_cmd.as_ref() else {
panic!("Expected `warp agent run-ambient` to parse as RunCloud");
};
}
#[test]
fn agent_update_rejects_conflicting_remove_flags() {
let result = Args::try_parse_from([
"warp",
"agent",
"update",
"agent_123",
"--description",
"new",
"--remove-description",
]);
assert!(result.is_err());
}
#[test]
fn agent_update_rejects_remove_all_secret_deltas() {
let result = Args::try_parse_from([
"warp",
"agent",
"update",
"agent_123",
"--add-secret",
"GITHUB_TOKEN",
"--remove-all-secrets",
]);
assert!(result.is_err());
}
#[test]
fn agent_run_rejects_prompt_and_task_id() {
let result = Args::try_parse_from([
"warp",
"agent",
"run",
"--prompt",
"hello",
"--task-id",
"d1b9b002-a8e1-422a-9016-e62490cb6a59",
]);
assert!(result.is_err());
}
#[test]
fn agent_run_rejects_without_prompt_or_task_id() {
let result = Args::try_parse_from(["warp", "agent", "run", "--model", "gpt-4o"]);
assert!(result.is_err());
let err = result.unwrap_err();
let err_str = err.to_string();
assert!(err_str.contains("prompt_group") || err_str.contains("required"));
}
#[test]
fn agent_run_accepts_prompt_only() {
let args = Args::try_parse_from(["warp", "agent", "run", "--prompt", "hello"]).unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp agent run` command");
};
let CliCommand::Agent(AgentCommand::Run(run_args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp agent run` command");
};
assert_eq!(run_args.prompt_arg.prompt.as_deref(), Some("hello"));
assert!(run_args.prompt_arg.saved_prompt.is_none());
assert!(run_args.skill.is_none());
assert!(run_args.task_id.is_none());
}
#[test]
fn agent_run_accepts_saved_prompt_only() {
let args = Args::try_parse_from(["warp", "agent", "run", "--saved-prompt", "sp-123"]).unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp agent run` command");
};
let CliCommand::Agent(AgentCommand::Run(run_args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp agent run` command");
};
assert!(run_args.prompt_arg.prompt.is_none());
assert_eq!(run_args.prompt_arg.saved_prompt.as_deref(), Some("sp-123"));
assert!(run_args.skill.is_none());
assert!(run_args.task_id.is_none());
}
#[test]
fn agent_run_accepts_skill_only() {
let args = Args::try_parse_from(["warp", "agent", "run", "--skill", "my-skill"]).unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp agent run` command");
};
let CliCommand::Agent(AgentCommand::Run(run_args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp agent run` command");
};
assert!(run_args.prompt_arg.prompt.is_none());
assert!(run_args.skill.is_some());
assert!(run_args.task_id.is_none());
}
#[test]
fn agent_run_accepts_task_id_only() {
let args = Args::try_parse_from(["warp", "agent", "run", "--task-id", "tid-456"]).unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp agent run` command");
};
let CliCommand::Agent(AgentCommand::Run(run_args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp agent run` command");
};
assert!(run_args.prompt_arg.prompt.is_none());
assert!(run_args.skill.is_none());
assert_eq!(run_args.task_id.as_deref(), Some("tid-456"));
}
#[test]
fn agent_run_accepts_prompt_and_skill() {
let args = Args::try_parse_from([
"warp", "agent", "run", "--prompt", "do stuff", "--skill", "my-skill",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp agent run` command");
};
let CliCommand::Agent(AgentCommand::Run(run_args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp agent run` command");
};
assert_eq!(run_args.prompt_arg.prompt.as_deref(), Some("do stuff"));
assert!(run_args.skill.is_some());
}
#[test]
fn agent_run_accepts_saved_prompt_and_skill() {
let args = Args::try_parse_from([
"warp",
"agent",
"run",
"--saved-prompt",
"sp-1",
"--skill",
"my-skill",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp agent run` command");
};
let CliCommand::Agent(AgentCommand::Run(run_args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp agent run` command");
};
assert_eq!(run_args.prompt_arg.saved_prompt.as_deref(), Some("sp-1"));
assert!(run_args.skill.is_some());
}
#[test]
fn agent_run_rejects_saved_prompt_and_task_id() {
let result = Args::try_parse_from([
"warp",
"agent",
"run",
"--saved-prompt",
"sp-1",
"--task-id",
"tid-1",
]);
assert!(result.is_err());
}
#[test]
fn agent_run_rejects_file_and_task_id() {
let result = Args::try_parse_from([
"warp",
"agent",
"run",
"--task-id",
"tid-1",
"--file",
"config.yaml",
]);
assert!(result.is_err());
}
#[test]
fn agent_run_accepts_skill_and_task_id() {
let args = Args::try_parse_from([
"warp",
"agent",
"run",
"--skill",
"my-skill",
"--task-id",
"tid-1",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp agent run` command");
};
let CliCommand::Agent(AgentCommand::Run(run_args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp agent run` command");
};
assert!(run_args.prompt_arg.prompt.is_none());
assert!(run_args.skill.is_some());
assert_eq!(run_args.task_id.as_deref(), Some("tid-1"));
}
#[test]
fn agent_run_rejects_prompt_and_saved_prompt() {
let result = Args::try_parse_from([
"warp",
"agent",
"run",
"--prompt",
"hello",
"--saved-prompt",
"sp-1",
]);
assert!(result.is_err());
}
#[test]
fn schedule_create_accepts_file() {
let args = Args::try_parse_from([
"warp",
"schedule",
"create",
"--name",
"test",
"--cron",
"0 9 * * 1",
"--prompt",
"hello",
"--file",
"schedule.yml",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp schedule create` command");
};
let CliCommand::Schedule(schedule_cmd) = boxed_cmd.as_ref() else {
panic!("Expected `warp schedule create` command");
};
let Some(ScheduleSubcommand::Create(create_args)) = schedule_cmd.subcommand() else {
panic!("Expected `warp schedule create` subcommand");
};
assert_eq!(
create_args
.config_file
.file
.as_ref()
.and_then(|p| p.to_str()),
Some("schedule.yml")
);
}
#[test]
fn schedule_resume_alias_parses_as_unpause() {
let args = Args::try_parse_from(["warp", "schedule", "resume", "schedule-id"]).unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp schedule resume` command");
};
let CliCommand::Schedule(schedule_cmd) = boxed_cmd.as_ref() else {
panic!("Expected `warp schedule resume` command");
};
let Some(ScheduleSubcommand::Unpause(unpause_args)) = schedule_cmd.subcommand() else {
panic!("Expected `warp schedule resume` to parse as `unpause`");
};
assert_eq!(unpause_args.schedule_id, "schedule-id");
}
#[test]
fn artifact_upload_accepts_run_id() {
let args = Args::try_parse_from([
"warp",
"artifact",
"upload",
"path/to/file.json",
"--run-id",
"run-123",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp artifact upload` command");
};
let CliCommand::Artifact(ArtifactCommand::Upload(args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp artifact upload` command");
};
assert_eq!(args.path.to_str(), Some("path/to/file.json"));
assert_eq!(args.run_id.as_deref(), Some("run-123"));
assert_eq!(args.conversation_id, None);
}
#[test]
fn artifact_help_hides_upload_but_keeps_download_visible() {
galaxy_core::features::mark_initialized();
let mut command = Args::clap_command();
command.build();
let artifact = command
.find_subcommand("artifact")
.expect("artifact subcommand should exist");
let upload = artifact
.find_subcommand("upload")
.expect("upload subcommand should exist");
let download = artifact
.find_subcommand("download")
.expect("download subcommand should exist");
let get = artifact
.find_subcommand("get")
.expect("get subcommand should exist");
assert!(upload.is_hide_set());
assert!(!get.is_hide_set());
assert!(!download.is_hide_set());
let visible_subcommands: Vec<_> = artifact
.get_subcommands()
.filter(|subcommand| !subcommand.is_hide_set())
.map(|subcommand| subcommand.get_name())
.collect();
assert!(visible_subcommands.contains(&"get"));
assert!(visible_subcommands.contains(&"download"));
assert!(!visible_subcommands.contains(&"upload"));
}
#[test]
fn raw_command_keeps_message_visible_before_runtime_help_customization() {
let mut command = <Args as clap::CommandFactory>::command();
command.build();
let run = command
.find_subcommand("run")
.expect("run subcommand should exist");
let message = run
.find_subcommand("message")
.expect("message subcommand should exist");
assert!(!message.is_hide_set());
let visible_subcommands: Vec<_> = run
.get_subcommands()
.filter(|subcommand| !subcommand.is_hide_set())
.map(|subcommand| subcommand.get_name())
.collect();
assert!(visible_subcommands.contains(&"message"));
}
#[test]
fn artifact_upload_accepts_run_id_and_description() {
let args = Args::try_parse_from([
"warp",
"artifact",
"upload",
"path/to/file.json",
"--run-id",
"run-123",
"--description",
"Test artifact",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp artifact upload` command");
};
let CliCommand::Artifact(ArtifactCommand::Upload(args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp artifact upload` command");
};
assert_eq!(args.run_id.as_deref(), Some("run-123"));
assert_eq!(args.conversation_id, None);
assert_eq!(args.description.as_deref(), Some("Test artifact"));
}
#[test]
fn artifact_upload_accepts_conversation_id_and_description() {
let args = Args::try_parse_from([
"warp",
"artifact",
"upload",
"path/to/file.json",
"--conversation-id",
"conversation-123",
"--description",
"Test artifact",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp artifact upload` command");
};
let CliCommand::Artifact(ArtifactCommand::Upload(args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp artifact upload` command");
};
assert_eq!(args.path.to_str(), Some("path/to/file.json"));
assert_eq!(args.run_id, None);
assert_eq!(args.conversation_id.as_deref(), Some("conversation-123"));
assert_eq!(args.description.as_deref(), Some("Test artifact"));
}
#[test]
fn artifact_upload_accepts_missing_association_target_for_env_fallback() {
let args = Args::try_parse_from(["warp", "artifact", "upload", "path/to/file.json"]).unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp artifact upload` command");
};
let CliCommand::Artifact(ArtifactCommand::Upload(args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp artifact upload` command");
};
assert_eq!(args.path.to_str(), Some("path/to/file.json"));
assert_eq!(args.run_id, None);
assert_eq!(args.conversation_id, None);
}
#[test]
fn artifact_upload_rejects_both_association_targets() {
let err = Args::try_parse_from([
"warp",
"artifact",
"upload",
"path/to/file.json",
"--run-id",
"run-123",
"--conversation-id",
"conversation-123",
])
.unwrap_err();
let err = err.to_string();
assert!(err.contains("--run-id"));
assert!(err.contains("--conversation-id"));
}
#[test]
fn artifact_download_parses_artifact_id_and_out() {
let args = Args::try_parse_from([
"warp",
"artifact",
"download",
"artifact-123",
"--out",
"downloads/file.json",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp artifact download` command");
};
let CliCommand::Artifact(ArtifactCommand::Download(args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp artifact download` command");
};
assert_eq!(args.artifact_uid, "artifact-123");
assert_eq!(
args.out.as_ref().and_then(|path| path.to_str()),
Some("downloads/file.json")
);
}
#[test]
fn artifact_get_parses_artifact_uid() {
let args = Args::try_parse_from(["warp", "artifact", "get", "artifact-123"]).unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp artifact get` command");
};
let CliCommand::Artifact(ArtifactCommand::Get(args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp artifact get` command");
};
assert_eq!(args.artifact_uid, "artifact-123");
}
#[test]
fn integration_create_accepts_file() {
let args = Args::try_parse_from([
"warp",
"integration",
"create",
"slack",
"--file",
"integration.yml",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp integration create` command");
};
let CliCommand::Integration(IntegrationCommand::Create(args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp integration create` command");
};
assert_eq!(
args.config_file.file.as_ref().and_then(|p| p.to_str()),
Some("integration.yml")
);
}
#[test]
fn integration_create_accepts_model() {
let args = Args::try_parse_from([
"warp",
"integration",
"create",
"slack",
"--model",
"gpt-4o",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp integration create` command");
};
let CliCommand::Integration(IntegrationCommand::Create(args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp integration create` command");
};
assert_eq!(args.model.model.as_deref(), Some("gpt-4o"));
}
#[test]
fn integration_update_accepts_file() {
let args = Args::try_parse_from([
"warp",
"integration",
"update",
"slack",
"--file",
"integration.json",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp integration update` command");
};
let CliCommand::Integration(IntegrationCommand::Update(args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp integration update` command");
};
assert_eq!(
args.config_file.file.as_ref().and_then(|p| p.to_str()),
Some("integration.json")
);
}
#[test]
fn integration_update_accepts_model() {
let args = Args::try_parse_from([
"warp",
"integration",
"update",
"slack",
"--model",
"gpt-4o",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp integration update` command");
};
let CliCommand::Integration(IntegrationCommand::Update(args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp integration update` command");
};
assert_eq!(args.model.model.as_deref(), Some("gpt-4o"));
}
#[test]
fn integration_create_accepts_mcp_json() {
let json = r#"{"my-server":{"command":"echo"}}"#;
let args =
Args::try_parse_from(["warp", "integration", "create", "slack", "--mcp", json]).unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp integration create` command");
};
let CliCommand::Integration(IntegrationCommand::Create(args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp integration create` command");
};
assert!(matches!(
args.mcp_specs.as_slice(),
[crate::mcp::MCPSpec::Json(parsed_json)] if parsed_json == json
));
}
#[test]
fn integration_update_accepts_mcp_json_and_remove_mcp() {
let json = r#"{"my-server":{"command":"echo"}}"#;
let args = Args::try_parse_from([
"warp",
"integration",
"update",
"slack",
"--mcp",
json,
"--remove-mcp",
"existing",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp integration update` command");
};
let CliCommand::Integration(IntegrationCommand::Update(args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp integration update` command");
};
assert!(matches!(
args.mcp_specs.as_slice(),
[crate::mcp::MCPSpec::Json(parsed_json)] if parsed_json == json
));
assert_eq!(args.remove_mcp, vec!["existing".to_string()]);
}
#[test]
fn schedule_create_accepts_mcp_json() {
let json = r#"{"my-server":{"command":"echo"}}"#;
let args = Args::try_parse_from([
"warp",
"schedule",
"create",
"--name",
"test",
"--cron",
"0 9 * * 1",
"--prompt",
"hello",
"--mcp",
json,
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp schedule create` command");
};
let CliCommand::Schedule(schedule_cmd) = boxed_cmd.as_ref() else {
panic!("Expected `warp schedule create` command");
};
let Some(ScheduleSubcommand::Create(create_args)) = schedule_cmd.subcommand() else {
panic!("Expected `warp schedule create` subcommand");
};
assert!(matches!(
create_args.mcp_specs.as_slice(),
[crate::mcp::MCPSpec::Json(parsed_json)] if parsed_json == json
));
}
#[test]
fn schedule_create_accepts_team_scope() {
let args = Args::try_parse_from([
"warp",
"schedule",
"create",
"--name",
"test",
"--cron",
"0 9 * * 1",
"--prompt",
"hello",
"--team",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp schedule create` command");
};
let CliCommand::Schedule(schedule_cmd) = boxed_cmd.as_ref() else {
panic!("Expected `warp schedule create` command");
};
let Some(ScheduleSubcommand::Create(create_args)) = schedule_cmd.subcommand() else {
panic!("Expected `warp schedule create` subcommand");
};
assert!(create_args.scope.team);
assert!(!create_args.scope.personal);
}
#[test]
fn schedule_create_accepts_personal_scope() {
let args = Args::try_parse_from([
"warp",
"schedule",
"create",
"--name",
"test",
"--cron",
"0 9 * * 1",
"--prompt",
"hello",
"--personal",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp schedule create` command");
};
let CliCommand::Schedule(schedule_cmd) = boxed_cmd.as_ref() else {
panic!("Expected `warp schedule create` command");
};
let Some(ScheduleSubcommand::Create(create_args)) = schedule_cmd.subcommand() else {
panic!("Expected `warp schedule create` subcommand");
};
assert!(!create_args.scope.team);
assert!(create_args.scope.personal);
}
#[test]
fn schedule_create_rejects_multiple_scopes() {
assert!(
Args::try_parse_from([
"warp",
"schedule",
"create",
"--name",
"test",
"--cron",
"0 9 * * 1",
"--prompt",
"hello",
"--team",
"--personal",
])
.is_err()
);
}
#[test]
fn schedule_update_accepts_file() {
let args = Args::try_parse_from([
"warp",
"schedule",
"update",
"schedule-id",
"--file",
"schedule.json",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp schedule update` command");
};
let CliCommand::Schedule(schedule_cmd) = boxed_cmd.as_ref() else {
panic!("Expected `warp schedule update` command");
};
let Some(ScheduleSubcommand::Update(update_args)) = schedule_cmd.subcommand() else {
panic!("Expected `warp schedule update` subcommand");
};
assert_eq!(
update_args
.config_file
.file
.as_ref()
.and_then(|p| p.to_str()),
Some("schedule.json")
);
}
#[test]
fn schedule_update_accepts_mcp_json_and_remove_mcp() {
let json = r#"{"my-server":{"command":"echo"}}"#;
let args = Args::try_parse_from([
"warp",
"schedule",
"update",
"schedule-id",
"--mcp",
json,
"--remove-mcp",
"existing",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp schedule update` command");
};
let CliCommand::Schedule(schedule_cmd) = boxed_cmd.as_ref() else {
panic!("Expected `warp schedule update` command");
};
let Some(ScheduleSubcommand::Update(update_args)) = schedule_cmd.subcommand() else {
panic!("Expected `warp schedule update` subcommand");
};
assert!(matches!(
update_args.mcp_specs.as_slice(),
[crate::mcp::MCPSpec::Json(parsed_json)] if parsed_json == json
));
assert_eq!(update_args.remove_mcp, vec!["existing".to_string()]);
}
#[test]
fn environment_image_list_parses() {
let args = Args::try_parse_from(["warp", "environment", "image", "list"]).unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp environment image list` command");
};
let CliCommand::Environment(EnvironmentCommand::Image(image_cmd)) = boxed_cmd.as_ref() else {
panic!("Expected `warp environment image` command");
};
assert!(matches!(image_cmd, ImageCommand::List));
}
#[test]
fn environment_create_accepts_description() {
let args = Args::try_parse_from([
"warp",
"environment",
"create",
"--name",
"test-env",
"--description",
"A test environment",
"--docker-image",
"ubuntu:latest",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp environment create` command");
};
let CliCommand::Environment(EnvironmentCommand::Create {
name,
description,
docker_image,
..
}) = boxed_cmd.as_ref()
else {
panic!("Expected `warp environment create` command");
};
assert_eq!(name, "test-env");
assert_eq!(description.as_deref(), Some("A test environment"));
assert_eq!(docker_image.as_deref(), Some("ubuntu:latest"));
}
#[test]
fn environment_create_description_max_length() {
// 240 characters should be accepted
let valid_description = "a".repeat(240);
let args = Args::try_parse_from([
"warp",
"environment",
"create",
"--name",
"test-env",
"--description",
&valid_description,
"--docker-image",
"ubuntu:latest",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp environment create` command");
};
let CliCommand::Environment(EnvironmentCommand::Create { description, .. }) =
boxed_cmd.as_ref()
else {
panic!("Expected `warp environment create` command");
};
assert_eq!(description.as_deref(), Some(valid_description.as_str()));
// 241 characters should be rejected
let invalid_description = "a".repeat(241);
assert!(
Args::try_parse_from([
"warp",
"environment",
"create",
"--name",
"test-env",
"--description",
&invalid_description,
"--docker-image",
"ubuntu:latest",
])
.is_err()
);
}
#[test]
fn environment_update_accepts_description() {
let args = Args::try_parse_from([
"warp",
"environment",
"update",
"env-id",
"--description",
"Updated description",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp environment update` command");
};
let CliCommand::Environment(EnvironmentCommand::Update {
id,
description,
remove_description,
..
}) = boxed_cmd.as_ref()
else {
panic!("Expected `warp environment update` command");
};
assert_eq!(id, "env-id");
assert_eq!(description.as_deref(), Some("Updated description"));
assert!(!remove_description);
}
#[test]
fn environment_update_accepts_remove_description() {
let args = Args::try_parse_from([
"warp",
"environment",
"update",
"env-id",
"--remove-description",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp environment update` command");
};
let CliCommand::Environment(EnvironmentCommand::Update {
id,
description,
remove_description,
..
}) = boxed_cmd.as_ref()
else {
panic!("Expected `warp environment update` command");
};
assert_eq!(id, "env-id");
assert!(description.is_none());
assert!(remove_description);
}
#[test]
fn agent_run_accepts_computer_use_flag() {
let args = Args::try_parse_from([
"warp",
"agent",
"run",
"--prompt",
"hello",
"--computer-use",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp agent run` command");
};
let CliCommand::Agent(AgentCommand::Run(run_args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp agent run` command");
};
assert!(run_args.computer_use.computer_use);
assert!(!run_args.computer_use.no_computer_use);
assert_eq!(run_args.computer_use.computer_use_override(), Some(true));
}
#[test]
fn agent_run_accepts_no_computer_use_flag() {
let args = Args::try_parse_from([
"warp",
"agent",
"run",
"--prompt",
"hello",
"--no-computer-use",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp agent run` command");
};
let CliCommand::Agent(AgentCommand::Run(run_args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp agent run` command");
};
assert!(!run_args.computer_use.computer_use);
assert!(run_args.computer_use.no_computer_use);
assert_eq!(run_args.computer_use.computer_use_override(), Some(false));
}
#[test]
fn agent_run_rejects_both_computer_use_flags() {
let result = Args::try_parse_from([
"warp",
"agent",
"run",
"--prompt",
"hello",
"--computer-use",
"--no-computer-use",
]);
assert!(result.is_err());
}
#[test]
fn agent_run_defaults_to_no_computer_use_override() {
let args = Args::try_parse_from(["warp", "agent", "run", "--prompt", "hello"]).unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp agent run` command");
};
let CliCommand::Agent(AgentCommand::Run(run_args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp agent run` command");
};
assert!(!run_args.computer_use.computer_use);
assert!(!run_args.computer_use.no_computer_use);
assert_eq!(run_args.computer_use.computer_use_override(), None);
}
#[test]
fn agent_run_cloud_accepts_snapshot_flags() {
let args = Args::try_parse_from([
"warp",
"agent",
"run-cloud",
"--prompt",
"hello",
"--no-snapshot",
"--snapshot-upload-timeout",
"2m",
"--snapshot-script-timeout",
"1m",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp agent run-cloud` command");
};
let CliCommand::Agent(AgentCommand::RunCloud(run_args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp agent run-cloud` command");
};
assert!(run_args.snapshot.no_snapshot);
assert_eq!(
run_args.snapshot.snapshot_upload_timeout,
Some(humantime::Duration::from(std::time::Duration::from_secs(
120
)))
);
assert_eq!(
run_args.snapshot.snapshot_script_timeout,
Some(humantime::Duration::from(std::time::Duration::from_secs(
60
)))
);
}
#[test]
fn agent_run_accepts_task_id_with_conversation_for_worker_followups() {
let args = Args::try_parse_from([
"warp",
"agent",
"run",
"--task-id",
"task-123",
"--conversation",
"conv-123",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp agent run` command");
};
let CliCommand::Agent(AgentCommand::Run(run_args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp agent run` command");
};
assert_eq!(run_args.task_id.as_deref(), Some("task-123"));
assert_eq!(run_args.conversation.as_deref(), Some("conv-123"));
}
#[test]
fn agent_run_cloud_accepts_computer_use_flag() {
let args = Args::try_parse_from([
"warp",
"agent",
"run-cloud",
"--prompt",
"hello",
"--computer-use",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp agent run-cloud` command");
};
let CliCommand::Agent(AgentCommand::RunCloud(run_args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp agent run-cloud` command");
};
assert!(run_args.computer_use.computer_use);
assert!(!run_args.computer_use.no_computer_use);
assert_eq!(run_args.computer_use.computer_use_override(), Some(true));
}
#[test]
fn agent_run_cloud_accepts_no_computer_use_flag() {
let args = Args::try_parse_from([
"warp",
"agent",
"run-cloud",
"--prompt",
"hello",
"--no-computer-use",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp agent run-cloud` command");
};
let CliCommand::Agent(AgentCommand::RunCloud(run_args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp agent run-cloud` command");
};
assert!(!run_args.computer_use.computer_use);
assert!(run_args.computer_use.no_computer_use);
assert_eq!(run_args.computer_use.computer_use_override(), Some(false));
}
#[test]
fn agent_run_cloud_rejects_both_computer_use_flags() {
let result = Args::try_parse_from([
"warp",
"agent",
"run-cloud",
"--prompt",
"hello",
"--computer-use",
"--no-computer-use",
]);
assert!(result.is_err());
}
#[test]
fn agent_run_cloud_defaults_to_no_computer_use_override() {
let args = Args::try_parse_from(["warp", "agent", "run-cloud", "--prompt", "hello"]).unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp agent run-cloud` command");
};
let CliCommand::Agent(AgentCommand::RunCloud(run_args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp agent run-cloud` command");
};
assert!(!run_args.computer_use.computer_use);
assert!(!run_args.computer_use.no_computer_use);
assert_eq!(run_args.computer_use.computer_use_override(), None);
}
#[test]
fn agent_run_cloud_accepts_harness_flag() {
let args = Args::try_parse_from([
"warp",
"agent",
"run-cloud",
"--prompt",
"hello",
"--harness",
"claude",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp agent run-cloud` command");
};
let CliCommand::Agent(AgentCommand::RunCloud(run_args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp agent run-cloud` command");
};
assert_eq!(run_args.harness, Harness::Claude);
}
#[test]
fn agent_run_cloud_defaults_harness_to_oz() {
let args = Args::try_parse_from(["warp", "agent", "run-cloud", "--prompt", "hello"]).unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp agent run-cloud` command");
};
let CliCommand::Agent(AgentCommand::RunCloud(run_args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp agent run-cloud` command");
};
assert_eq!(run_args.harness, Harness::Oz);
}
#[test]
fn harness_parse_orchestration_harness_accepts_aliases() {
assert_eq!(
Harness::parse_orchestration_harness("claude-code"),
Some(Harness::Claude)
);
assert_eq!(
Harness::parse_orchestration_harness("open_code"),
Some(Harness::OpenCode)
);
}
#[test]
fn harness_parse_local_child_harness_rejects_oz() {
assert_eq!(Harness::parse_local_child_harness("oz"), None);
assert_eq!(
Harness::parse_local_child_harness("opencode"),
Some(Harness::OpenCode)
);
}
#[test]
fn harness_parse_orchestration_harness_accepts_codex() {
assert_eq!(
Harness::parse_orchestration_harness("codex"),
Some(Harness::Codex)
);
}
#[test]
fn harness_parse_local_child_harness_accepts_codex() {
assert_eq!(
Harness::parse_local_child_harness("codex"),
Some(Harness::Codex)
);
}
#[test]
fn agent_run_cloud_accepts_claude_auth_secret_with_harness() {
let args = Args::try_parse_from([
"warp",
"agent",
"run-cloud",
"--prompt",
"hello",
"--harness",
"claude",
"--claude-auth-secret",
"my-key",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp agent run-cloud` command");
};
let CliCommand::Agent(AgentCommand::RunCloud(run_args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp agent run-cloud` command");
};
assert_eq!(run_args.harness, Harness::Claude);
assert_eq!(run_args.claude_auth_secret.as_deref(), Some("my-key"));
}
#[test]
fn agent_run_cloud_claude_auth_secret_without_harness_parses() {
// Clap parsing succeeds; runtime validation (in mod.rs) rejects this combination.
let args = Args::try_parse_from([
"warp",
"agent",
"run-cloud",
"--prompt",
"hello",
"--claude-auth-secret",
"my-key",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp agent run-cloud` command");
};
let CliCommand::Agent(AgentCommand::RunCloud(run_args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp agent run-cloud` command");
};
assert_eq!(run_args.harness, Harness::Oz);
assert_eq!(run_args.claude_auth_secret.as_deref(), Some("my-key"));
}
#[test]
fn run_message_send_parses() {
let args = Args::try_parse_from([
"warp",
"run",
"message",
"send",
"--to",
"run-1",
"--to",
"run-2",
"--subject",
"Build update",
"--body",
"Done",
"--sender-run-id",
"sender-1",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp run message send` command");
};
let CliCommand::Run(TaskCommand::Message(MessageCommand::Send(send_args))) = boxed_cmd.as_ref()
else {
panic!("Expected `warp run message send` command");
};
assert_eq!(send_args.to, vec!["run-1".to_string(), "run-2".to_string()]);
assert_eq!(send_args.subject, "Build update");
assert_eq!(send_args.body, "Done");
assert_eq!(send_args.sender_run_id, "sender-1");
}
#[test]
fn run_message_list_parses_filters() {
let args = Args::try_parse_from([
"warp",
"run",
"message",
"list",
"run-123",
"--unread",
"--since",
"2026-04-09T20:00:00Z",
"--limit",
"25",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp run message list` command");
};
let CliCommand::Run(TaskCommand::Message(MessageCommand::List(list_args))) = boxed_cmd.as_ref()
else {
panic!("Expected `warp run message list` command");
};
assert_eq!(list_args.run_id, "run-123");
assert!(list_args.unread);
assert_eq!(list_args.since.as_deref(), Some("2026-04-09T20:00:00Z"));
assert_eq!(list_args.limit, 25);
}
#[test]
fn run_message_list_rejects_non_positive_limit() {
assert!(
Args::try_parse_from(["warp", "run", "message", "list", "run-123", "--limit", "0",])
.is_err()
);
}
#[test]
fn run_message_watch_parses() {
let args = Args::try_parse_from([
"warp",
"run",
"message",
"watch",
"--output-format",
"ndjson",
"run-123",
"--since-sequence",
"7",
])
.unwrap();
assert_eq!(args.global_options.output_format, OutputFormat::Ndjson);
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp run message watch` command");
};
let CliCommand::Run(TaskCommand::Message(MessageCommand::Watch(watch_args))) =
boxed_cmd.as_ref()
else {
panic!("Expected `warp run message watch` command");
};
assert_eq!(watch_args.run_id, "run-123");
assert_eq!(watch_args.since_sequence, 7);
}
#[test]
fn run_message_read_parses() {
let args = Args::try_parse_from(["warp", "run", "message", "read", "message-123"]).unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp run message read` command");
};
let CliCommand::Run(TaskCommand::Message(MessageCommand::Read(read_args))) = boxed_cmd.as_ref()
else {
panic!("Expected `warp run message read` command");
};
assert_eq!(read_args.message_id, "message-123");
}
#[test]
fn run_message_mark_delivered_parses() {
let args =
Args::try_parse_from(["warp", "run", "message", "mark-delivered", "message-456"]).unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp run message mark-delivered` command");
};
let CliCommand::Run(TaskCommand::Message(MessageCommand::MarkDelivered(delivered_args))) =
boxed_cmd.as_ref()
else {
panic!("Expected `warp run message mark-delivered` command");
};
assert_eq!(delivered_args.message_id, "message-456");
}
#[test]
#[serial_test::serial]
fn hidden_server_overrides_parse_from_env() {
let previous_server_root = set_env_var(SERVER_ROOT_URL_OVERRIDE_ENV, "http://localhost:8080");
let previous_ws = set_env_var(WS_SERVER_URL_OVERRIDE_ENV, "ws://localhost:8082/graphql/v2");
let previous_session_sharing = set_env_var(
SESSION_SHARING_SERVER_URL_OVERRIDE_ENV,
"ws://127.0.0.1:8081",
);
let args = Args::try_parse_from(["warp", "whoami"]).unwrap();
restore_env_var(SERVER_ROOT_URL_OVERRIDE_ENV, previous_server_root);
restore_env_var(WS_SERVER_URL_OVERRIDE_ENV, previous_ws);
restore_env_var(
SESSION_SHARING_SERVER_URL_OVERRIDE_ENV,
previous_session_sharing,
);
assert_eq!(args.server_root_url(), Some("http://localhost:8080"));
assert_eq!(args.ws_server_url(), Some("ws://localhost:8082/graphql/v2"));
assert_eq!(
args.session_sharing_server_url(),
Some("ws://127.0.0.1:8081")
);
}
#[test]
fn run_message_delivered_alias_parses() {
let args =
Args::try_parse_from(["warp", "run", "message", "delivered", "message-456"]).unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp run message delivered` command");
};
let CliCommand::Run(TaskCommand::Message(MessageCommand::MarkDelivered(delivered_args))) =
boxed_cmd.as_ref()
else {
panic!("Expected `warp run message delivered` command");
};
assert_eq!(delivered_args.message_id, "message-456");
}
#[test]
fn finish_task_accepts_status_success() {
let args = Args::try_parse_from([
"warp",
"harness-support",
"--run-id",
"run-1",
"finish-task",
"--status",
"success",
"--summary",
"all good",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected harness-support command");
};
let CliCommand::HarnessSupport(hs_args) = boxed_cmd.as_ref() else {
panic!("Expected harness-support command");
};
let HarnessSupportCommand::FinishTask(finish_args) = &hs_args.command else {
panic!("Expected finish-task subcommand");
};
assert_eq!(finish_args.status, TaskStatus::Success);
assert_eq!(finish_args.summary, "all good");
}
#[test]
fn finish_task_accepts_status_failure() {
let args = Args::try_parse_from([
"warp",
"harness-support",
"--run-id",
"run-1",
"finish-task",
"--status",
"failure",
"--summary",
"something broke",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected harness-support command");
};
let CliCommand::HarnessSupport(hs_args) = boxed_cmd.as_ref() else {
panic!("Expected harness-support command");
};
let HarnessSupportCommand::FinishTask(finish_args) = &hs_args.command else {
panic!("Expected finish-task subcommand");
};
assert_eq!(finish_args.status, TaskStatus::Failure);
assert_eq!(finish_args.summary, "something broke");
}
#[test]
fn finish_task_rejects_invalid_status() {
let result = Args::try_parse_from([
"warp",
"harness-support",
"--run-id",
"run-1",
"finish-task",
"--status",
"maybe",
"--summary",
"who knows",
]);
assert!(result.is_err());
}
#[test]
fn finish_task_rejects_missing_status() {
let result = Args::try_parse_from([
"warp",
"harness-support",
"--run-id",
"run-1",
"finish-task",
"--summary",
"no status",
]);
assert!(result.is_err());
}
#[test]
fn report_shutdown_clean_parses() {
let args = Args::try_parse_from([
"warp",
"harness-support",
"--run-id",
"run-1",
"report-shutdown",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected harness-support command");
};
let CliCommand::HarnessSupport(hs_args) = boxed_cmd.as_ref() else {
panic!("Expected harness-support command");
};
let HarnessSupportCommand::ReportShutdown(shutdown_args) = &hs_args.command else {
panic!("Expected report-shutdown subcommand");
};
assert!(shutdown_args.error_category.is_none());
assert!(shutdown_args.error_message.is_none());
}
#[test]
fn secret_create_codex_api_key_parses_minimal() {
galaxy_core::features::mark_initialized();
let args = Args::try_parse_from([
"warp",
"secret",
"create",
"codex",
"api-key",
"my-openai-key",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp secret create codex api-key` command");
};
let CliCommand::Secret(SecretCommand::Create(create_args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp secret create` command");
};
let Some(CreateProvider::Codex(codex)) = &create_args.provider else {
panic!("Expected `codex` provider subcommand");
};
let CodexMethod::ApiKey(api_key_args) = &codex.method;
assert_eq!(api_key_args.common.name, "my-openai-key");
assert!(api_key_args.common.description.is_none());
assert!(api_key_args.value.value_file.is_none());
assert!(api_key_args.base_url.is_none());
}
#[test]
fn secret_create_codex_api_key_accepts_base_url_and_value_file() {
galaxy_core::features::mark_initialized();
let args = Args::try_parse_from([
"warp",
"secret",
"create",
"codex",
"api-key",
"my-openai-key",
"--value-file",
"key.txt",
"--base-url",
"https://us.api.openai.com/v1",
"--description",
"OpenAI key for Codex",
"--team",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected `warp secret create codex api-key` command");
};
let CliCommand::Secret(SecretCommand::Create(create_args)) = boxed_cmd.as_ref() else {
panic!("Expected `warp secret create` command");
};
let Some(CreateProvider::Codex(codex)) = &create_args.provider else {
panic!("Expected `codex` provider subcommand");
};
let CodexMethod::ApiKey(api_key_args) = &codex.method;
assert_eq!(api_key_args.common.name, "my-openai-key");
assert_eq!(
api_key_args.common.description.as_deref(),
Some("OpenAI key for Codex")
);
assert!(api_key_args.common.scope.team);
assert!(!api_key_args.common.scope.personal);
assert_eq!(
api_key_args
.value
.value_file
.as_ref()
.and_then(|p| p.to_str()),
Some("key.txt")
);
assert_eq!(
api_key_args.base_url.as_deref(),
Some("https://us.api.openai.com/v1")
);
}
#[test]
fn secret_create_codex_api_key_requires_name() {
galaxy_core::features::mark_initialized();
let result = Args::try_parse_from(["warp", "secret", "create", "codex", "api-key"]);
assert!(result.is_err());
}
#[test]
fn report_shutdown_abnormal_parses() {
let args = Args::try_parse_from([
"warp",
"harness-support",
"--run-id",
"run-1",
"report-shutdown",
"--error-category",
"oom",
"--error-message",
"out of memory",
])
.unwrap();
let Some(Command::CommandLine(boxed_cmd)) = args.command else {
panic!("Expected harness-support command");
};
let CliCommand::HarnessSupport(hs_args) = boxed_cmd.as_ref() else {
panic!("Expected harness-support command");
};
let HarnessSupportCommand::ReportShutdown(shutdown_args) = &hs_args.command else {
panic!("Expected report-shutdown subcommand");
};
assert_eq!(shutdown_args.error_category.as_deref(), Some("oom"));
assert_eq!(
shutdown_args.error_message.as_deref(),
Some("out of memory")
);
}