Test not-so-yolo CLI mode

This commit is contained in:
Joe Gershenson
2026-05-11 14:16:13 -07:00
parent 02805335a4
commit cb0c69c215
5 changed files with 175 additions and 0 deletions

View File

@@ -2218,6 +2218,52 @@ mod tests {
assert_eq!(interactive.resume_session_id, None);
}
#[test]
fn resume_merges_not_so_yolo_flag() {
let interactive = finalize_resume_from_args(["codex", "resume", "--not-so-yolo"].as_ref());
assert!(interactive.auto_review_cli_mode);
assert!(interactive.resume_picker);
assert!(!interactive.resume_last);
assert_eq!(interactive.resume_session_id, None);
}
#[test]
fn exec_inherits_root_not_so_yolo_flag() {
let cli = MultitoolCli::try_parse_from(["codex", "--not-so-yolo", "exec", "hello"])
.expect("parse");
let MultitoolCli {
interactive,
config_overrides: _,
feature_toggles: _,
remote: _,
subcommand,
} = cli;
let Some(Subcommand::Exec(mut exec_cli)) = subcommand else {
panic!("expected exec subcommand");
};
exec_cli
.shared
.inherit_exec_root_options(&interactive.shared);
assert!(exec_cli.auto_review_cli_mode);
assert!(!exec_cli.dangerously_bypass_approvals_and_sandbox);
}
#[test]
fn not_so_yolo_conflicts_with_approval_policy() {
let err = MultitoolCli::try_parse_from([
"codex",
"--not-so-yolo",
"--ask-for-approval",
"on-request",
])
.expect_err("conflicting permission flags should be rejected");
assert_eq!(err.kind(), clap::error::ErrorKind::ArgumentConflict);
}
#[test]
fn fork_picker_logic_none_and_not_last() {
let interactive = finalize_fork_from_args(["codex", "fork"].as_ref());

View File

@@ -35,6 +35,25 @@ fn resume_parses_prompt_after_global_flags() {
assert_eq!(effective_prompt.as_deref(), Some(PROMPT));
}
#[test]
fn resume_parses_prompt_after_not_so_yolo_global_flag() {
const PROMPT: &str = "echo resume-with-not-so-yolo-after-subcommand";
let cli = Cli::parse_from(["codex-exec", "resume", "--last", "--not-so-yolo", PROMPT]);
assert!(cli.auto_review_cli_mode);
let Some(Command::Resume(args)) = cli.command else {
panic!("expected resume command");
};
let effective_prompt = args.prompt.clone().or_else(|| {
if args.last {
args.session_id.clone()
} else {
None
}
});
assert_eq!(effective_prompt.as_deref(), Some(PROMPT));
}
#[test]
fn resume_accepts_output_last_message_flag_after_subcommand() {
const PROMPT: &str = "echo resume-with-output-file";

View File

@@ -4,6 +4,7 @@ mod apply_patch;
mod auth_env;
mod ephemeral;
mod mcp_required_exit;
mod not_so_yolo;
mod originator;
mod output_schema;
mod prompt_stdin;

View File

@@ -0,0 +1,46 @@
#![allow(clippy::unwrap_used, clippy::expect_used)]
use anyhow::Context;
use codex_utils_cargo_bin::find_resource;
use core_test_support::test_codex_exec::test_codex_exec;
fn exec_fixture() -> anyhow::Result<std::path::PathBuf> {
Ok(find_resource!("tests/fixtures/cli_responses_fixture.sse")?)
}
#[test]
fn not_so_yolo_uses_on_request_approvals_with_workspace_write() -> anyhow::Result<()> {
let test = test_codex_exec();
let fixture = exec_fixture()?;
let repo_root = codex_utils_cargo_bin::repo_root()?;
let output = test
.cmd()
.env("CODEX_RS_SSE_FIXTURE", &fixture)
.arg("--skip-git-repo-check")
.arg("--not-so-yolo")
.arg("-C")
.arg(&repo_root)
.arg("hello")
.output()
.context("not-so-yolo run should succeed")?;
assert!(output.status.success(), "run failed: {output:?}");
let stderr = String::from_utf8(output.stderr)?;
assert!(
stderr.contains("approval: on-request"),
"stderr missing on-request approval mode: {stderr}"
);
let expected_sandbox = if cfg!(target_os = "windows") {
"sandbox: read-only"
} else {
"sandbox: workspace-write"
};
assert!(
stderr.contains(expected_sandbox),
"stderr missing expected sandbox summary `{expected_sandbox}`: {stderr}"
);
Ok(())
}

View File

@@ -175,3 +175,66 @@ impl SharedCliOptions {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use clap::Parser;
use pretty_assertions::assert_eq;
#[derive(Debug, Parser)]
struct TestCli {
#[clap(flatten)]
shared: SharedCliOptions,
}
#[test]
fn parses_not_so_yolo() {
let cli = TestCli::parse_from(["test", "--not-so-yolo"]);
assert!(cli.shared.auto_review_cli_mode);
assert!(!cli.shared.dangerously_bypass_approvals_and_sandbox);
}
#[test]
fn yolo_and_not_so_yolo_conflict() {
let err = TestCli::try_parse_from(["test", "--yolo", "--not-so-yolo"])
.expect_err("permission modes should conflict");
assert_eq!(err.kind(), clap::error::ErrorKind::ArgumentConflict);
}
#[test]
fn not_so_yolo_inherits_to_exec_subcommand_options() {
let root = SharedCliOptions {
auto_review_cli_mode: true,
..Default::default()
};
let mut exec = SharedCliOptions::default();
exec.inherit_exec_root_options(&root);
assert!(exec.auto_review_cli_mode);
assert!(!exec.dangerously_bypass_approvals_and_sandbox);
}
#[test]
fn subcommand_permission_mode_blocks_root_not_so_yolo() {
let root = SharedCliOptions {
auto_review_cli_mode: true,
..Default::default()
};
let mut exec = SharedCliOptions {
sandbox_mode: Some(SandboxModeCliArg::ReadOnly),
..Default::default()
};
exec.inherit_exec_root_options(&root);
assert!(!exec.auto_review_cli_mode);
assert!(matches!(
exec.sandbox_mode,
Some(SandboxModeCliArg::ReadOnly)
));
}
}