app-server: add debug MDM overrides + stabilize analytics tests

Provide a debug-only escape hatch for managed-config state so tests are not
polluted by host MDM settings:
- Allow `CODEX_APP_SERVER_MANAGED_PREFERENCES_BASE64` and
  `CODEX_APP_SERVER_MANAGED_REQUIREMENTS_BASE64` to override loader inputs
  (debug builds only).
- Test harnesses now clear those env vars and the OpenAI socket override so
  child app-server processes start from a clean environment.
- Analytics tests use LoaderOverrides with empty managed preferences to avoid
  accidental MDM config injection.

This keeps app-server behavior unchanged in release while making the test
suite deterministic on managed machines.
This commit is contained in:
Iliyan Malchev
2026-02-12 07:25:34 -08:00
parent 828cf5c867
commit b349563da7
3 changed files with 53 additions and 9 deletions

View File

@@ -12,6 +12,8 @@ use std::path::PathBuf;
// Debug-only test hook: lets integration tests point the server at a temporary
// managed config file without writing to /etc.
const MANAGED_CONFIG_PATH_ENV_VAR: &str = "CODEX_APP_SERVER_MANAGED_CONFIG_PATH";
const MANAGED_PREFERENCES_BASE64_ENV_VAR: &str = "CODEX_APP_SERVER_MANAGED_PREFERENCES_BASE64";
const MANAGED_REQUIREMENTS_BASE64_ENV_VAR: &str = "CODEX_APP_SERVER_MANAGED_REQUIREMENTS_BASE64";
#[derive(Debug, Parser)]
struct AppServerArgs {
@@ -41,10 +43,17 @@ fn main() -> anyhow::Result<()> {
arg0_dispatch_or_else(|arg0_paths: Arg0DispatchPaths| async move {
let args = AppServerArgs::parse();
let managed_config_path = managed_config_path_from_debug_env();
let loader_overrides = LoaderOverrides {
let mut loader_overrides = LoaderOverrides {
managed_config_path,
..Default::default()
};
#[cfg(target_os = "macos")]
if let Some(value) = managed_preferences_base64_from_debug_env() {
loader_overrides.managed_preferences_base64 = Some(value);
}
if let Some(value) = managed_requirements_base64_from_debug_env() {
loader_overrides.macos_managed_config_requirements_base64 = Some(value);
}
let transport = args.listen;
let session_source = args.session_source;
let auth = args.auth.try_into_settings()?;
@@ -77,3 +86,23 @@ fn managed_config_path_from_debug_env() -> Option<PathBuf> {
None
}
fn managed_preferences_base64_from_debug_env() -> Option<String> {
#[cfg(debug_assertions)]
{
std::env::var(MANAGED_PREFERENCES_BASE64_ENV_VAR).ok()
}
#[cfg(not(debug_assertions))]
None
}
fn managed_requirements_base64_from_debug_env() -> Option<String> {
#[cfg(debug_assertions)]
{
std::env::var(MANAGED_REQUIREMENTS_BASE64_ENV_VAR).ok()
}
#[cfg(not(debug_assertions))]
None
}

View File

@@ -80,6 +80,7 @@ use codex_app_server_protocol::TurnStartParams;
use codex_app_server_protocol::TurnSteerParams;
use codex_app_server_protocol::WindowsSandboxSetupStartParams;
use codex_core::default_client::CODEX_INTERNAL_ORIGINATOR_OVERRIDE_ENV_VAR;
use codex_core::openai_socket::CODEX_OPENAI_UNIX_SOCKET_ENV_VAR;
use tokio::process::Command;
pub struct McpProcess {
@@ -95,6 +96,8 @@ pub struct McpProcess {
}
pub const DEFAULT_CLIENT_NAME: &str = "codex-app-server-tests";
const MANAGED_PREFERENCES_BASE64_ENV_VAR: &str = "CODEX_APP_SERVER_MANAGED_PREFERENCES_BASE64";
const MANAGED_REQUIREMENTS_BASE64_ENV_VAR: &str = "CODEX_APP_SERVER_MANAGED_REQUIREMENTS_BASE64";
impl McpProcess {
pub async fn new(codex_home: &Path) -> anyhow::Result<Self> {
@@ -133,6 +136,9 @@ impl McpProcess {
cmd.env("CODEX_HOME", codex_home);
cmd.env("RUST_LOG", "info");
cmd.env_remove(CODEX_INTERNAL_ORIGINATOR_OVERRIDE_ENV_VAR);
cmd.env_remove(CODEX_OPENAI_UNIX_SOCKET_ENV_VAR);
cmd.env(MANAGED_PREFERENCES_BASE64_ENV_VAR, "");
cmd.env(MANAGED_REQUIREMENTS_BASE64_ENV_VAR, "");
cmd.args(args);
for (k, v) in env_overrides {

View File

@@ -2,6 +2,7 @@ use anyhow::Result;
use codex_core::config::ConfigBuilder;
use codex_core::config::types::OtelExporterKind;
use codex_core::config::types::OtelHttpProtocol;
use codex_core::config_loader::LoaderOverrides;
use pretty_assertions::assert_eq;
use std::collections::HashMap;
use tempfile::TempDir;
@@ -17,13 +18,24 @@ fn set_metrics_exporter(config: &mut codex_core::config::Config) {
};
}
fn config_builder_without_mdm(codex_home: &TempDir) -> ConfigBuilder {
let mut loader_overrides = LoaderOverrides {
macos_managed_config_requirements_base64: Some(String::new()),
..Default::default()
};
#[cfg(target_os = "macos")]
{
loader_overrides.managed_preferences_base64 = Some(String::new());
}
ConfigBuilder::default()
.codex_home(codex_home.path().to_path_buf())
.loader_overrides(loader_overrides)
}
#[tokio::test]
async fn app_server_default_analytics_disabled_without_flag() -> Result<()> {
let codex_home = TempDir::new()?;
let mut config = ConfigBuilder::default()
.codex_home(codex_home.path().to_path_buf())
.build()
.await?;
let mut config = config_builder_without_mdm(&codex_home).build().await?;
set_metrics_exporter(&mut config);
config.analytics_enabled = None;
@@ -45,10 +57,7 @@ async fn app_server_default_analytics_disabled_without_flag() -> Result<()> {
#[tokio::test]
async fn app_server_default_analytics_enabled_with_flag() -> Result<()> {
let codex_home = TempDir::new()?;
let mut config = ConfigBuilder::default()
.codex_home(codex_home.path().to_path_buf())
.build()
.await?;
let mut config = config_builder_without_mdm(&codex_home).build().await?;
set_metrics_exporter(&mut config);
config.analytics_enabled = None;