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 { 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) { 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 = ::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") ); }