mirror of
https://github.com/openai/codex.git
synced 2026-04-25 23:24:55 +00:00
121 lines
3.9 KiB
Rust
121 lines
3.9 KiB
Rust
use std::collections::HashMap;
|
|
use std::path::Path;
|
|
use std::time::Duration;
|
|
use tokio::select;
|
|
use tokio::time::timeout;
|
|
|
|
/// Regression test for https://github.com/openai/codex/issues/8803.
|
|
#[tokio::test]
|
|
#[ignore = "TODO(mbolin): flaky"]
|
|
async fn malformed_rules_should_not_panic() -> anyhow::Result<()> {
|
|
// run_codex_cli() does not work on Windows due to PTY limitations.
|
|
if cfg!(windows) {
|
|
return Ok(());
|
|
}
|
|
|
|
let tmp = tempfile::tempdir()?;
|
|
let codex_home = tmp.path();
|
|
std::fs::write(
|
|
codex_home.join("rules"),
|
|
"rules should be a directory not a file",
|
|
)?;
|
|
|
|
// TODO(mbolin): Figure out why using a temp dir as the cwd causes this test
|
|
// to hang.
|
|
let cwd = std::env::current_dir()?;
|
|
let config_contents = format!(
|
|
r#"
|
|
# Pick a local provider so the CLI doesn't prompt for OpenAI auth in this test.
|
|
model_provider = "ollama"
|
|
|
|
[projects]
|
|
"{cwd}" = {{ trust_level = "trusted" }}
|
|
"#,
|
|
cwd = cwd.display()
|
|
);
|
|
std::fs::write(codex_home.join("config.toml"), config_contents)?;
|
|
|
|
let CodexCliOutput { exit_code, output } = run_codex_cli(codex_home, cwd).await?;
|
|
assert_ne!(0, exit_code, "Codex CLI should exit nonzero.");
|
|
assert!(
|
|
output.contains("ERROR: Failed to initialize codex:"),
|
|
"expected startup error in output, got: {output}"
|
|
);
|
|
assert!(
|
|
output.contains("failed to read rules files"),
|
|
"expected rules read error in output, got: {output}"
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
struct CodexCliOutput {
|
|
exit_code: i32,
|
|
output: String,
|
|
}
|
|
|
|
async fn run_codex_cli(
|
|
codex_home: impl AsRef<Path>,
|
|
cwd: impl AsRef<Path>,
|
|
) -> anyhow::Result<CodexCliOutput> {
|
|
let codex_cli = codex_utils_cargo_bin::cargo_bin("codex")?;
|
|
let mut env = HashMap::new();
|
|
env.insert(
|
|
"CODEX_HOME".to_string(),
|
|
codex_home.as_ref().display().to_string(),
|
|
);
|
|
|
|
let args = vec!["-c".to_string(), "analytics.enabled=false".to_string()];
|
|
let spawned = codex_utils_pty::spawn_pty_process(
|
|
codex_cli.to_string_lossy().as_ref(),
|
|
&args,
|
|
cwd.as_ref(),
|
|
&env,
|
|
&None,
|
|
)
|
|
.await?;
|
|
let mut output = Vec::new();
|
|
let mut output_rx = spawned.output_rx;
|
|
let mut exit_rx = spawned.exit_rx;
|
|
let writer_tx = spawned.session.writer_sender();
|
|
let exit_code_result = timeout(Duration::from_secs(10), async {
|
|
// Read PTY output until the process exits while replying to cursor
|
|
// position queries so the TUI can initialize without a real terminal.
|
|
loop {
|
|
select! {
|
|
result = output_rx.recv() => match result {
|
|
Ok(chunk) => {
|
|
// The TUI asks for the cursor position via ESC[6n.
|
|
// Respond with a valid position to unblock startup.
|
|
if chunk.windows(4).any(|window| window == b"\x1b[6n") {
|
|
let _ = writer_tx.send(b"\x1b[1;1R".to_vec()).await;
|
|
}
|
|
output.extend_from_slice(&chunk);
|
|
}
|
|
Err(tokio::sync::broadcast::error::RecvError::Closed) => break exit_rx.await,
|
|
Err(tokio::sync::broadcast::error::RecvError::Lagged(_)) => {}
|
|
},
|
|
result = &mut exit_rx => break result,
|
|
}
|
|
}
|
|
})
|
|
.await;
|
|
let exit_code = match exit_code_result {
|
|
Ok(Ok(code)) => code,
|
|
Ok(Err(err)) => return Err(err.into()),
|
|
Err(_) => {
|
|
spawned.session.terminate();
|
|
anyhow::bail!("timed out waiting for codex CLI to exit");
|
|
}
|
|
};
|
|
// Drain any output that raced with the exit notification.
|
|
while let Ok(chunk) = output_rx.try_recv() {
|
|
output.extend_from_slice(&chunk);
|
|
}
|
|
|
|
let output = String::from_utf8_lossy(&output);
|
|
Ok(CodexCliOutput {
|
|
exit_code,
|
|
output: output.to_string(),
|
|
})
|
|
}
|