Add codex exec testing helpers (#4254)
Add a shortcut to create working directories and run codex exec with fake server.
This commit is contained in:
@@ -8,6 +8,7 @@ path = "lib.rs"
|
||||
|
||||
[dependencies]
|
||||
anyhow = { workspace = true }
|
||||
assert_cmd = { workspace = true }
|
||||
codex-core = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
tempfile = { workspace = true }
|
||||
|
||||
@@ -9,6 +9,7 @@ use codex_core::config::ConfigToml;
|
||||
|
||||
pub mod responses;
|
||||
pub mod test_codex;
|
||||
pub mod test_codex_exec;
|
||||
|
||||
/// Returns a default `Config` whose on-disk state is confined to the provided
|
||||
/// temporary directory. Using a per-test directory keeps tests hermetic and
|
||||
|
||||
@@ -2,6 +2,7 @@ use serde_json::Value;
|
||||
use wiremock::BodyPrintLimit;
|
||||
use wiremock::Mock;
|
||||
use wiremock::MockServer;
|
||||
use wiremock::Respond;
|
||||
use wiremock::ResponseTemplate;
|
||||
use wiremock::matchers::method;
|
||||
use wiremock::matchers::path;
|
||||
@@ -131,3 +132,41 @@ pub async fn start_mock_server() -> MockServer {
|
||||
.start()
|
||||
.await
|
||||
}
|
||||
|
||||
/// Mounts a sequence of SSE response bodies and serves them in order for each
|
||||
/// POST to `/v1/responses`. Panics if more requests are received than bodies
|
||||
/// provided. Also asserts the exact number of expected calls.
|
||||
pub async fn mount_sse_sequence(server: &MockServer, bodies: Vec<String>) {
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
struct SeqResponder {
|
||||
num_calls: AtomicUsize,
|
||||
responses: Vec<String>,
|
||||
}
|
||||
|
||||
impl Respond for SeqResponder {
|
||||
fn respond(&self, _: &wiremock::Request) -> ResponseTemplate {
|
||||
let call_num = self.num_calls.fetch_add(1, Ordering::SeqCst);
|
||||
match self.responses.get(call_num) {
|
||||
Some(body) => ResponseTemplate::new(200)
|
||||
.insert_header("content-type", "text/event-stream")
|
||||
.set_body_string(body.clone()),
|
||||
None => panic!("no response for {call_num}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let num_calls = bodies.len();
|
||||
let responder = SeqResponder {
|
||||
num_calls: AtomicUsize::new(0),
|
||||
responses: bodies,
|
||||
};
|
||||
|
||||
Mock::given(method("POST"))
|
||||
.and(path("/v1/responses"))
|
||||
.respond_with(responder)
|
||||
.expect(num_calls as u64)
|
||||
.mount(server)
|
||||
.await;
|
||||
}
|
||||
|
||||
40
codex-rs/core/tests/common/test_codex_exec.rs
Normal file
40
codex-rs/core/tests/common/test_codex_exec.rs
Normal file
@@ -0,0 +1,40 @@
|
||||
#![allow(clippy::expect_used)]
|
||||
use std::path::Path;
|
||||
use tempfile::TempDir;
|
||||
use wiremock::MockServer;
|
||||
|
||||
pub struct TestCodexExecBuilder {
|
||||
home: TempDir,
|
||||
cwd: TempDir,
|
||||
}
|
||||
|
||||
impl TestCodexExecBuilder {
|
||||
pub fn cmd(&self) -> assert_cmd::Command {
|
||||
let mut cmd = assert_cmd::Command::cargo_bin("codex-exec")
|
||||
.expect("should find binary for codex-exec");
|
||||
cmd.current_dir(self.cwd.path())
|
||||
.env("CODEX_HOME", self.home.path())
|
||||
.env("OPENAI_API_KEY", "dummy");
|
||||
cmd
|
||||
}
|
||||
pub fn cmd_with_server(&self, server: &MockServer) -> assert_cmd::Command {
|
||||
let mut cmd = self.cmd();
|
||||
let base = format!("{}/v1", server.uri());
|
||||
cmd.env("OPENAI_BASE_URL", base);
|
||||
cmd
|
||||
}
|
||||
|
||||
pub fn cwd_path(&self) -> &Path {
|
||||
self.cwd.path()
|
||||
}
|
||||
pub fn home_path(&self) -> &Path {
|
||||
self.home.path()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn test_codex_exec() -> TestCodexExecBuilder {
|
||||
TestCodexExecBuilder {
|
||||
home: TempDir::new().expect("create temp home"),
|
||||
cwd: TempDir::new().expect("create temp cwd"),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user