mirror of
https://github.com/openai/codex.git
synced 2026-05-01 03:42:05 +03:00
Add remote env CI matrix and integration test (#14869)
`CODEX_TEST_REMOTE_ENV` will make `test_codex` start the executor "remotely" (inside a docker container) turning any integration test into remote test.
This commit is contained in:
@@ -2277,14 +2277,9 @@ async fn code_mode_can_call_hidden_dynamic_tools() -> Result<()> {
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
let test = TestCodex {
|
||||
home: base_test.home,
|
||||
cwd: base_test.cwd,
|
||||
codex: new_thread.thread,
|
||||
session_configured: new_thread.session_configured,
|
||||
config: base_test.config,
|
||||
thread_manager: base_test.thread_manager,
|
||||
};
|
||||
let mut test = base_test;
|
||||
test.codex = new_thread.thread;
|
||||
test.session_configured = new_thread.session_configured;
|
||||
|
||||
let code = r#"
|
||||
import { ALL_TOOLS, hidden_dynamic_tool } from "tools.js";
|
||||
|
||||
@@ -103,6 +103,7 @@ mod prompt_caching;
|
||||
mod quota_exceeded;
|
||||
mod read_file;
|
||||
mod realtime_conversation;
|
||||
mod remote_env;
|
||||
mod remote_models;
|
||||
mod request_compression;
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
|
||||
57
codex-rs/core/tests/suite/remote_env.rs
Normal file
57
codex-rs/core/tests/suite/remote_env.rs
Normal file
@@ -0,0 +1,57 @@
|
||||
use anyhow::Result;
|
||||
use codex_exec_server::RemoveOptions;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use core_test_support::get_remote_test_env;
|
||||
use core_test_support::test_codex::test_env;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::path::PathBuf;
|
||||
use std::time::SystemTime;
|
||||
use std::time::UNIX_EPOCH;
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn remote_test_env_can_connect_and_use_filesystem() -> Result<()> {
|
||||
let Some(_remote_env) = get_remote_test_env() else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let test_env = test_env().await?;
|
||||
let file_system = test_env.environment().get_filesystem();
|
||||
|
||||
let file_path = remote_test_file_path();
|
||||
let file_path_abs = absolute_path(file_path.clone())?;
|
||||
let payload = b"remote-test-env-ok".to_vec();
|
||||
|
||||
file_system
|
||||
.write_file(&file_path_abs, payload.clone())
|
||||
.await?;
|
||||
let actual = file_system.read_file(&file_path_abs).await?;
|
||||
assert_eq!(actual, payload);
|
||||
|
||||
file_system
|
||||
.remove(
|
||||
&file_path_abs,
|
||||
RemoveOptions {
|
||||
recursive: false,
|
||||
force: true,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn absolute_path(path: PathBuf) -> Result<AbsolutePathBuf> {
|
||||
AbsolutePathBuf::try_from(path.clone())
|
||||
.map_err(|err| anyhow::anyhow!("invalid absolute path {}: {err}", path.display()))
|
||||
}
|
||||
|
||||
fn remote_test_file_path() -> PathBuf {
|
||||
let nanos = match SystemTime::now().duration_since(UNIX_EPOCH) {
|
||||
Ok(duration) => duration.as_nanos(),
|
||||
Err(_) => 0,
|
||||
};
|
||||
PathBuf::from(format!(
|
||||
"/tmp/codex-remote-test-env-{}-{nanos}.txt",
|
||||
std::process::id()
|
||||
))
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
use base64::Engine;
|
||||
use base64::engine::general_purpose::STANDARD as BASE64_STANDARD;
|
||||
use codex_core::CodexAuth;
|
||||
use codex_exec_server::CreateDirectoryOptions;
|
||||
use codex_features::Feature;
|
||||
use codex_protocol::config_types::ReasoningSummary;
|
||||
use codex_protocol::openai_models::ConfigShellToolType;
|
||||
@@ -32,12 +33,16 @@ use core_test_support::test_codex::TestCodex;
|
||||
use core_test_support::test_codex::test_codex;
|
||||
use core_test_support::wait_for_event;
|
||||
use core_test_support::wait_for_event_with_timeout;
|
||||
use image::DynamicImage;
|
||||
use image::GenericImageView;
|
||||
use image::ImageBuffer;
|
||||
use image::Rgba;
|
||||
use image::load_from_memory;
|
||||
use pretty_assertions::assert_eq;
|
||||
use serde_json::Value;
|
||||
use std::io::Cursor;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use tokio::time::Duration;
|
||||
use wiremock::BodyPrintLimit;
|
||||
use wiremock::MockServer;
|
||||
@@ -73,6 +78,11 @@ fn find_image_message(body: &Value) -> Option<&Value> {
|
||||
image_messages(body).into_iter().next()
|
||||
}
|
||||
|
||||
fn absolute_path(path: &Path) -> anyhow::Result<codex_utils_absolute_path::AbsolutePathBuf> {
|
||||
codex_utils_absolute_path::AbsolutePathBuf::try_from(path.to_path_buf())
|
||||
.map_err(|err| anyhow::anyhow!("invalid absolute path {}: {err}", path.display()))
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn user_turn_with_local_image_attaches_image() -> anyhow::Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
@@ -171,23 +181,37 @@ async fn view_image_tool_attaches_local_image() -> anyhow::Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
|
||||
let server = start_mock_server().await;
|
||||
|
||||
let mut builder = test_codex();
|
||||
let test = builder.build_remote_aware(&server).await?;
|
||||
let TestCodex {
|
||||
codex,
|
||||
cwd,
|
||||
session_configured,
|
||||
config,
|
||||
..
|
||||
} = test_codex().build(&server).await?;
|
||||
} = &test;
|
||||
let cwd = config.cwd.clone();
|
||||
|
||||
let rel_path = PathBuf::from("assets/example.png");
|
||||
let abs_path = cwd.join(&rel_path);
|
||||
let abs_path_absolute = absolute_path(&abs_path)?;
|
||||
let assets_dir = cwd.join("assets");
|
||||
|
||||
let file_system = test.fs();
|
||||
|
||||
let rel_path = "assets/example.png";
|
||||
let abs_path = cwd.path().join(rel_path);
|
||||
if let Some(parent) = abs_path.parent() {
|
||||
std::fs::create_dir_all(parent)?;
|
||||
}
|
||||
let original_width = 2304;
|
||||
let original_height = 864;
|
||||
let image = ImageBuffer::from_pixel(original_width, original_height, Rgba([255u8, 0, 0, 255]));
|
||||
image.save(&abs_path)?;
|
||||
let mut cursor = Cursor::new(Vec::new());
|
||||
DynamicImage::ImageRgba8(image).write_to(&mut cursor, image::ImageFormat::Png)?;
|
||||
file_system
|
||||
.create_directory(
|
||||
&absolute_path(&assets_dir)?,
|
||||
CreateDirectoryOptions { recursive: true },
|
||||
)
|
||||
.await?;
|
||||
file_system
|
||||
.write_file(&abs_path_absolute, cursor.into_inner())
|
||||
.await?;
|
||||
|
||||
let call_id = "view-image-call";
|
||||
let arguments = serde_json::json!({ "path": rel_path }).to_string();
|
||||
@@ -214,7 +238,7 @@ async fn view_image_tool_attaches_local_image() -> anyhow::Result<()> {
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: cwd.path().to_path_buf(),
|
||||
cwd: cwd.clone(),
|
||||
approval_policy: AskForApproval::Never,
|
||||
sandbox_policy: SandboxPolicy::DangerFullAccess,
|
||||
model: session_model,
|
||||
@@ -228,7 +252,7 @@ async fn view_image_tool_attaches_local_image() -> anyhow::Result<()> {
|
||||
|
||||
let mut tool_event = None;
|
||||
wait_for_event_with_timeout(
|
||||
&codex,
|
||||
codex,
|
||||
|event| match event {
|
||||
EventMsg::ViewImageToolCall(_) => {
|
||||
tool_event = Some(event.clone());
|
||||
|
||||
Reference in New Issue
Block a user