Files
codex/codex-rs/app-server-daemon/src/managed_install_tests.rs
T
Eric Traut 1a169eda11 Distinguish daemon release identity from executable contents (#50559)
## Why

Releases can contain different resources while shipping identical executable bytes. The daemon must recognize those release changes, while a stale updater should be able to hand off without restarting an already-current daemon.

## What changed

- Include a digest of the resolved executable path in `ExecutableIdentity` to distinguish release generations with identical binaries.
- Keep content-only comparisons for package preparation and manual update reporting.
- Check the running daemon's identity and version before restarting during updater adoption, manual updates, and production restoration. Preserve scheduled retries when the daemon is not ready.

## Testing

Add coverage for path-sensitive identity, scheduled no-op updates that preserve the daemon, and resource-only release updates that restart it despite identical executable bytes.

GitOrigin-RevId: bbea06cf502abb2251e70d4ba4b422cae867116c
2026-10-03 07:17:20 +00:00

56 lines
2.0 KiB
Rust

use pretty_assertions::assert_eq;
use super::ExecutableIdentity;
use super::executable_identity;
use super::parse_codex_version;
#[test]
fn parses_codex_cli_version_output() {
assert_eq!(
parse_codex_version("codex 1.2.3\n").expect("version"),
"1.2.3"
);
}
#[test]
fn rejects_malformed_codex_cli_version_output() {
assert!(parse_codex_version("codex\n").is_err());
}
#[tokio::test]
async fn executable_identity_uses_path_and_binary_contents() {
let directory = tempfile::tempdir().expect("temporary directory");
let executable = directory.path().join("codex");
// Span multiple reads, including a partial final buffer, and preserve the
// digest stored by older clients that hashed the complete file in memory.
let mut bytes: Vec<u8> = (0..200_003).map(|index| (index % 251) as u8).collect();
for contents in [&bytes[..], &[][..]] {
std::fs::write(&executable, contents).expect("write executable");
assert_eq!(
executable_identity(&executable).await.expect("identity"),
ExecutableIdentity {
digest: *blake3::hash(contents).as_bytes(),
path_digest: Some(super::path_digest(
&std::fs::canonicalize(&executable).expect("canonical executable"),
)),
}
);
}
let copy = directory.path().join("codex-copy");
std::fs::copy(&executable, &copy).expect("copy executable");
let identity = executable_identity(&executable).await.expect("identity");
let copy_identity = executable_identity(&copy).await.expect("copy identity");
assert_ne!(identity, copy_identity);
assert!(identity.same_contents(&copy_identity));
std::fs::write(&executable, &bytes).expect("write executable");
let old = executable_identity(&executable).await.expect("identity");
bytes[100_000] ^= 1;
std::fs::write(&executable, bytes).expect("replace executable");
assert_ne!(
executable_identity(&executable)
.await
.expect("new identity"),
old
);
}