mirror of
https://github.com/openai/codex.git
synced 2026-04-24 14:45:27 +00:00
This PR introduces a `codex-utils-cargo-bin` utility crate that wraps/replaces our use of `assert_cmd::Command` and `escargot::CargoBuild`. As you can infer from the introduction of `buck_project_root()` in this PR, I am attempting to make it possible to build Codex under [Buck2](https://buck2.build) as well as `cargo`. With Buck2, I hope to achieve faster incremental local builds (largely due to Buck2's [dice](https://buck2.build/docs/insights_and_knowledge/modern_dice/) build strategy, as well as benefits from its local build daemon) as well as faster CI builds if we invest in remote execution and caching. See https://buck2.build/docs/getting_started/what_is_buck2/#why-use-buck2-key-advantages for more details about the performance advantages of Buck2. Buck2 enforces stronger requirements in terms of build and test isolation. It discourages assumptions about absolute paths (which is key to enabling remote execution). Because the `CARGO_BIN_EXE_*` environment variables that Cargo provides are absolute paths (which `assert_cmd::Command` reads), this is a problem for Buck2, which is why we need this `codex-utils-cargo-bin` utility. My WIP-Buck2 setup sets the `CARGO_BIN_EXE_*` environment variables passed to a `rust_test()` build rule as relative paths. `codex-utils-cargo-bin` will resolve these values to absolute paths, when necessary. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/8496). * #8498 * __->__ #8496
151 lines
4.6 KiB
Rust
151 lines
4.6 KiB
Rust
#![allow(clippy::expect_used, clippy::unwrap_used, unused_imports)]
|
|
|
|
use anyhow::Context;
|
|
use assert_cmd::prelude::*;
|
|
use codex_core::CODEX_APPLY_PATCH_ARG1;
|
|
use core_test_support::responses::ev_apply_patch_custom_tool_call;
|
|
use core_test_support::responses::ev_apply_patch_function_call;
|
|
use core_test_support::responses::ev_completed;
|
|
use core_test_support::responses::mount_sse_sequence;
|
|
use core_test_support::responses::sse;
|
|
use core_test_support::responses::start_mock_server;
|
|
use std::fs;
|
|
use std::process::Command;
|
|
use tempfile::tempdir;
|
|
|
|
/// While we may add an `apply-patch` subcommand to the `codex` CLI multitool
|
|
/// at some point, we must ensure that the smaller `codex-exec` CLI can still
|
|
/// emulate the `apply_patch` CLI.
|
|
#[test]
|
|
fn test_standalone_exec_cli_can_use_apply_patch() -> anyhow::Result<()> {
|
|
let tmp = tempdir()?;
|
|
let relative_path = "source.txt";
|
|
let absolute_path = tmp.path().join(relative_path);
|
|
fs::write(&absolute_path, "original content\n")?;
|
|
|
|
Command::new(codex_utils_cargo_bin::cargo_bin("codex-exec")?)
|
|
.arg(CODEX_APPLY_PATCH_ARG1)
|
|
.arg(
|
|
r#"*** Begin Patch
|
|
*** Update File: source.txt
|
|
@@
|
|
-original content
|
|
+modified by apply_patch
|
|
*** End Patch"#,
|
|
)
|
|
.current_dir(tmp.path())
|
|
.assert()
|
|
.success()
|
|
.stdout("Success. Updated the following files:\nM source.txt\n")
|
|
.stderr(predicates::str::is_empty());
|
|
assert_eq!(
|
|
fs::read_to_string(absolute_path)?,
|
|
"modified by apply_patch\n"
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(not(target_os = "windows"))]
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
|
|
async fn test_apply_patch_tool() -> anyhow::Result<()> {
|
|
use core_test_support::skip_if_no_network;
|
|
use core_test_support::test_codex_exec::test_codex_exec;
|
|
|
|
skip_if_no_network!(Ok(()));
|
|
|
|
let test = test_codex_exec();
|
|
let tmp_path = test.cwd_path().to_path_buf();
|
|
let add_patch = r#"*** Begin Patch
|
|
*** Add File: test.md
|
|
+Hello world
|
|
*** End Patch"#;
|
|
let update_patch = r#"*** Begin Patch
|
|
*** Update File: test.md
|
|
@@
|
|
-Hello world
|
|
+Final text
|
|
*** End Patch"#;
|
|
let response_streams = vec![
|
|
sse(vec![
|
|
ev_apply_patch_custom_tool_call("request_0", add_patch),
|
|
ev_completed("request_0"),
|
|
]),
|
|
sse(vec![
|
|
ev_apply_patch_function_call("request_1", update_patch),
|
|
ev_completed("request_1"),
|
|
]),
|
|
sse(vec![ev_completed("request_2")]),
|
|
];
|
|
let server = start_mock_server().await;
|
|
mount_sse_sequence(&server, response_streams).await;
|
|
|
|
test.cmd_with_server(&server)
|
|
.arg("--skip-git-repo-check")
|
|
.arg("-s")
|
|
.arg("danger-full-access")
|
|
.arg("foo")
|
|
.assert()
|
|
.success();
|
|
|
|
let final_path = tmp_path.join("test.md");
|
|
let contents = std::fs::read_to_string(&final_path)
|
|
.unwrap_or_else(|e| panic!("failed reading {}: {e}", final_path.display()));
|
|
assert_eq!(contents, "Final text\n");
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(not(target_os = "windows"))]
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
|
|
async fn test_apply_patch_freeform_tool() -> anyhow::Result<()> {
|
|
use core_test_support::skip_if_no_network;
|
|
use core_test_support::test_codex_exec::test_codex_exec;
|
|
|
|
skip_if_no_network!(Ok(()));
|
|
|
|
let test = test_codex_exec();
|
|
let freeform_add_patch = r#"*** Begin Patch
|
|
*** Add File: app.py
|
|
+class BaseClass:
|
|
+ def method():
|
|
+ return False
|
|
*** End Patch"#;
|
|
let freeform_update_patch = r#"*** Begin Patch
|
|
*** Update File: app.py
|
|
@@ def method():
|
|
- return False
|
|
+
|
|
+ return True
|
|
*** End Patch"#;
|
|
let response_streams = vec![
|
|
sse(vec![
|
|
ev_apply_patch_custom_tool_call("request_0", freeform_add_patch),
|
|
ev_completed("request_0"),
|
|
]),
|
|
sse(vec![
|
|
ev_apply_patch_custom_tool_call("request_1", freeform_update_patch),
|
|
ev_completed("request_1"),
|
|
]),
|
|
sse(vec![ev_completed("request_2")]),
|
|
];
|
|
let server = start_mock_server().await;
|
|
mount_sse_sequence(&server, response_streams).await;
|
|
|
|
test.cmd_with_server(&server)
|
|
.arg("--skip-git-repo-check")
|
|
.arg("-s")
|
|
.arg("danger-full-access")
|
|
.arg("foo")
|
|
.assert()
|
|
.success();
|
|
|
|
// Verify final file contents
|
|
let final_path = test.cwd_path().join("app.py");
|
|
let contents = std::fs::read_to_string(&final_path)
|
|
.unwrap_or_else(|e| panic!("failed reading {}: {e}", final_path.display()));
|
|
assert_eq!(
|
|
contents,
|
|
include_str!("../fixtures/apply_patch_freeform_final.txt")
|
|
);
|
|
Ok(())
|
|
}
|