codex: fix notify helper resolution in app-server tests (#13534)

This commit is contained in:
Ahmed Ibrahim
2026-03-05 02:38:33 -08:00
parent 380c8f5ff3
commit 18a943582b
6 changed files with 91 additions and 32 deletions

View File

@@ -3,6 +3,5 @@ load("//:defs.bzl", "codex_rust_crate")
codex_rust_crate(
name = "app-server",
crate_name = "codex_app_server",
extra_binaries = ["//codex-rs/app-server/tests/common:codex-app-server-test-notify-capture"],
test_tags = ["no-sandbox"],
)

View File

@@ -8,6 +8,10 @@ license.workspace = true
name = "codex-app-server"
path = "src/main.rs"
[[bin]]
name = "codex-app-server-test-notify-capture"
path = "src/bin/notify_capture.rs"
[lib]
name = "codex_app_server"
path = "src/lib.rs"

View File

@@ -0,0 +1,61 @@
use std::env;
use std::fs::File;
use std::fs::OpenOptions;
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;
use anyhow::Context;
use anyhow::Result;
use anyhow::anyhow;
use anyhow::bail;
fn append_log(log_path: &Path, message: &str) {
if let Ok(mut file) = OpenOptions::new().create(true).append(true).open(log_path) {
let _ = writeln!(file, "{message}");
let _ = file.sync_all();
}
}
fn main() -> Result<()> {
let mut args = env::args_os();
let _program = args.next();
let output_path = PathBuf::from(
args.next()
.ok_or_else(|| anyhow!("expected output path as first argument"))?,
);
let log_path = PathBuf::from(
args.next()
.ok_or_else(|| anyhow!("expected log path as second argument"))?,
);
let payload = args
.next()
.ok_or_else(|| anyhow!("expected payload as final argument"))?;
append_log(
&log_path,
&format!(
"start cwd={} output={}",
env::current_dir()?.display(),
output_path.display()
),
);
if args.next().is_some() {
append_log(&log_path, "unexpected extra argument");
bail!("expected payload as final argument");
}
let payload = payload.to_string_lossy();
append_log(&log_path, &format!("payload-bytes={}", payload.len()));
let mut file = File::create(&output_path)
.with_context(|| format!("failed to create {}", output_path.display()))?;
file.write_all(payload.as_bytes())
.with_context(|| format!("failed to write {}", output_path.display()))?;
file.sync_all()
.with_context(|| format!("failed to sync {}", output_path.display()))?;
append_log(&log_path, &format!("wrote {}", output_path.display()));
Ok(())
}

View File

@@ -7,10 +7,6 @@ license.workspace = true
[lib]
path = "lib.rs"
[[bin]]
name = "codex-app-server-test-notify-capture"
path = "src/bin/notify_capture.rs"
[dependencies]
anyhow = { workspace = true }
base64 = { workspace = true }

View File

@@ -1,22 +0,0 @@
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
fn main() -> anyhow::Result<()> {
let mut args = env::args_os();
let _program = args.next();
let output_path = PathBuf::from(
args.next()
.ok_or_else(|| anyhow::anyhow!("expected output path as first argument"))?,
);
let payload = args
.next()
.ok_or_else(|| anyhow::anyhow!("expected payload as final argument"))?;
let mut file = File::create(&output_path)?;
file.write_all(payload.to_string_lossy().as_bytes())?;
file.sync_all()?;
Ok(())
}

View File

@@ -194,6 +194,7 @@ async fn turn_start_notify_payload_includes_initialize_client_name() -> Result<(
let server = create_mock_responses_server_sequence_unchecked(responses).await;
let codex_home = TempDir::new()?;
let notify_file = codex_home.path().join("notify.json");
let notify_log = codex_home.path().join("notify.log");
let notify_capture = cargo_bin("codex-app-server-test-notify-capture")?;
let notify_capture = notify_capture
.to_str()
@@ -201,14 +202,18 @@ async fn turn_start_notify_payload_includes_initialize_client_name() -> Result<(
let notify_file_str = notify_file
.to_str()
.expect("notify file path should be valid UTF-8");
let notify_log_str = notify_log
.to_str()
.expect("notify log path should be valid UTF-8");
create_config_toml_with_extra(
codex_home.path(),
&server.uri(),
"never",
&format!(
"notify = [{}, {}]",
"notify = [{}, {}, {}]",
toml_basic_string(notify_capture),
toml_basic_string(notify_file_str)
toml_basic_string(notify_file_str),
toml_basic_string(notify_log_str)
),
)?;
@@ -256,27 +261,43 @@ async fn turn_start_notify_payload_includes_initialize_client_name() -> Result<(
)
.await??;
let payload = wait_for_json_file(&notify_file).await?;
let payload = wait_for_json_file(&notify_file, &notify_log).await?;
assert_eq!(payload["client"], "xcode");
Ok(())
}
async fn wait_for_json_file(path: &Path) -> Result<Value> {
async fn wait_for_json_file(path: &Path, log_path: &Path) -> Result<Value> {
let deadline = Instant::now() + Duration::from_secs(5);
let mut last_contents = None;
loop {
match tokio::fs::read_to_string(path).await {
Ok(contents) => {
if let Ok(payload) = serde_json::from_str(&contents) {
return Ok(payload);
}
last_contents = Some(contents);
}
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {}
Err(err) => return Err(err.into()),
}
if Instant::now() >= deadline {
anyhow::bail!("timed out waiting for valid JSON in {}", path.display());
let helper_log = match tokio::fs::read_to_string(log_path).await {
Ok(contents) => contents,
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
"<missing helper log>".to_string()
}
Err(err) => format!("<failed to read helper log: {err}>"),
};
let last_contents =
last_contents.unwrap_or_else(|| "<missing notify file>".to_string());
anyhow::bail!(
"timed out waiting for valid JSON in {}. helper log: {} last contents: {}",
path.display(),
helper_log.trim_end(),
last_contents.trim_end()
);
}
sleep(Duration::from_millis(25)).await;