Remove test-support feature from codex-core and replace it with explicit test toggles (#11405)

## Why

`codex-core` was being built in multiple feature-resolved permutations
because test-only behavior was modeled as crate features. For a large
crate, those permutations increase compile cost and reduce cache reuse.

## Net Change

- Removed the `test-support` crate feature and related feature wiring so
`codex-core` no longer needs separate feature shapes for test consumers.
- Standardized cross-crate test-only access behind
`codex_core::test_support`.
- External test code now imports helpers from
`codex_core::test_support`.
- Underlying implementation hooks are kept internal (`pub(crate)`)
instead of broadly public.

## Outcome

- Fewer `codex-core` build permutations.
- Better incremental cache reuse across test targets.
- No intended production behavior change.
This commit is contained in:
Michael Bolin
2026-02-10 22:44:02 -08:00
committed by GitHub
parent f6dd9e37e7
commit 476c1a7160
36 changed files with 393 additions and 266 deletions

View File

@@ -1,4 +1,3 @@
use codex_core::AuthManager;
use codex_core::CodexAuth;
use codex_core::ContentItem;
use codex_core::LocalShellAction;
@@ -17,7 +16,6 @@ use codex_core::built_in_model_providers;
use codex_core::default_client::originator;
use codex_core::error::CodexErr;
use codex_core::features::Feature;
use codex_core::models_manager::manager::ModelsManager;
use codex_core::protocol::EventMsg;
use codex_core::protocol::Op;
use codex_core::protocol::SessionSource;
@@ -573,7 +571,7 @@ async fn prefers_apikey_when_config_prefers_apikey_even_with_chatgpt_tokens() {
let auth_manager =
match CodexAuth::from_auth_storage(codex_home.path(), AuthCredentialsStoreMode::File) {
Ok(Some(auth)) => codex_core::AuthManager::from_auth_for_testing(auth),
Ok(Some(auth)) => codex_core::test_support::auth_manager_from_auth(auth),
Ok(None) => panic!("No CodexAuth found in codex_home"),
Err(e) => panic!("Failed to load CodexAuth: {e}"),
};
@@ -1318,12 +1316,14 @@ async fn azure_responses_request_includes_store_and_reasoning_ids() {
config.model_provider = provider.clone();
let effort = config.model_reasoning_effort;
let summary = config.model_reasoning_summary;
let model = ModelsManager::get_model_offline(config.model.as_deref());
let model = codex_core::test_support::get_model_offline(config.model.as_deref());
config.model = Some(model.clone());
let config = Arc::new(config);
let model_info = ModelsManager::construct_model_info_offline(model.as_str(), &config);
let model_info =
codex_core::test_support::construct_model_info_offline(model.as_str(), &config);
let conversation_id = ThreadId::new();
let auth_manager = AuthManager::from_auth_for_testing(CodexAuth::from_api_key("Test API Key"));
let auth_manager =
codex_core::test_support::auth_manager_from_auth(CodexAuth::from_api_key("Test API Key"));
let otel_manager = OtelManager::new(
conversation_id,
model.as_str(),

View File

@@ -1,5 +1,4 @@
#![allow(clippy::expect_used, clippy::unwrap_used)]
use codex_core::AuthManager;
use codex_core::CodexAuth;
use codex_core::ContentItem;
use codex_core::ModelClient;
@@ -11,7 +10,6 @@ use codex_core::ResponseItem;
use codex_core::WireApi;
use codex_core::X_RESPONSESAPI_INCLUDE_TIMING_METRICS_HEADER;
use codex_core::features::Feature;
use codex_core::models_manager::manager::ModelsManager;
use codex_core::protocol::EventMsg;
use codex_core::protocol::Op;
use codex_core::protocol::SessionSource;
@@ -979,10 +977,11 @@ async fn websocket_harness_with_options(
config.features.enable(Feature::ResponsesWebsocketsV2);
}
let config = Arc::new(config);
let mut model_info = ModelsManager::construct_model_info_offline(MODEL, &config);
let mut model_info = codex_core::test_support::construct_model_info_offline(MODEL, &config);
model_info.prefer_websockets = prefer_websockets;
let conversation_id = ThreadId::new();
let auth_manager = AuthManager::from_auth_for_testing(CodexAuth::from_api_key("Test API Key"));
let auth_manager =
codex_core::test_support::auth_manager_from_auth(CodexAuth::from_api_key("Test API Key"));
let exporter = InMemoryMetricExporter::default();
let metrics = MetricsClient::new(
MetricsConfig::in_memory("test", "codex-core", env!("CARGO_PKG_VERSION"), exporter)

View File

@@ -1001,7 +1001,7 @@ async fn resume_conversation(
config: &Config,
path: std::path::PathBuf,
) -> Arc<CodexThread> {
let auth_manager = codex_core::AuthManager::from_auth_for_testing(
let auth_manager = codex_core::test_support::auth_manager_from_auth(
codex_core::CodexAuth::from_api_key("dummy"),
);
manager

View File

@@ -11,7 +11,7 @@ async fn offline_model_info_without_tool_output_override() {
let codex_home = TempDir::new().expect("create temp dir");
let mut config = load_default_config_for_test(&codex_home).await;
config.features.enable(Feature::RemoteModels);
let auth_manager = codex_core::AuthManager::from_auth_for_testing(
let auth_manager = codex_core::test_support::auth_manager_from_auth(
CodexAuth::create_dummy_chatgpt_auth_for_testing(),
);
let manager = ModelsManager::new(config.codex_home.clone(), auth_manager);
@@ -30,7 +30,7 @@ async fn offline_model_info_with_tool_output_override() {
let mut config = load_default_config_for_test(&codex_home).await;
config.features.enable(Feature::RemoteModels);
config.tool_output_token_limit = Some(123);
let auth_manager = codex_core::AuthManager::from_auth_for_testing(
let auth_manager = codex_core::test_support::auth_manager_from_auth(
CodexAuth::create_dummy_chatgpt_auth_for_testing(),
);
let manager = ModelsManager::new(config.codex_home.clone(), auth_manager);

View File

@@ -47,7 +47,7 @@ async fn personality_does_not_mutate_base_instructions_without_template() {
config.features.enable(Feature::Personality);
config.personality = Some(Personality::Friendly);
let model_info = ModelsManager::construct_model_info_offline("gpt-5.1", &config);
let model_info = codex_core::test_support::construct_model_info_offline("gpt-5.1", &config);
assert_eq!(
model_info.get_model_instructions(config.personality),
model_info.base_instructions
@@ -62,7 +62,8 @@ async fn base_instructions_override_disables_personality_template() {
config.personality = Some(Personality::Friendly);
config.base_instructions = Some("override instructions".to_string());
let model_info = ModelsManager::construct_model_info_offline("gpt-5.2-codex", &config);
let model_info =
codex_core::test_support::construct_model_info_offline("gpt-5.2-codex", &config);
assert_eq!(model_info.base_instructions, "override instructions");
assert_eq!(
@@ -470,7 +471,8 @@ async fn instructions_uses_base_if_feature_disabled() -> anyhow::Result<()> {
config.features.disable(Feature::Personality);
config.personality = Some(Personality::Friendly);
let model_info = ModelsManager::construct_model_info_offline("gpt-5.2-codex", &config);
let model_info =
codex_core::test_support::construct_model_info_offline("gpt-5.2-codex", &config);
assert_eq!(
model_info.get_model_instructions(config.personality),
model_info.base_instructions

View File

@@ -100,9 +100,9 @@ async fn remote_models_get_model_info_uses_longest_matching_prefix() -> Result<(
base_url: Some(format!("{}/v1", server.uri())),
..built_in_model_providers()["openai"].clone()
};
let manager = ModelsManager::with_provider(
let manager = codex_core::test_support::models_manager_with_provider(
codex_home.path().to_path_buf(),
codex_core::auth::AuthManager::from_auth_for_testing(auth),
codex_core::test_support::auth_manager_from_auth(auth),
provider,
);
@@ -497,9 +497,9 @@ async fn remote_models_preserve_builtin_presets() -> Result<()> {
base_url: Some(format!("{}/v1", server.uri())),
..built_in_model_providers()["openai"].clone()
};
let manager = ModelsManager::with_provider(
let manager = codex_core::test_support::models_manager_with_provider(
codex_home.path().to_path_buf(),
codex_core::auth::AuthManager::from_auth_for_testing(auth),
codex_core::test_support::auth_manager_from_auth(auth),
provider,
);
@@ -562,9 +562,9 @@ async fn remote_models_merge_adds_new_high_priority_first() -> Result<()> {
base_url: Some(format!("{}/v1", server.uri())),
..built_in_model_providers()["openai"].clone()
};
let manager = ModelsManager::with_provider(
let manager = codex_core::test_support::models_manager_with_provider(
codex_home.path().to_path_buf(),
codex_core::auth::AuthManager::from_auth_for_testing(auth),
codex_core::test_support::auth_manager_from_auth(auth),
provider,
);
@@ -613,9 +613,9 @@ async fn remote_models_merge_replaces_overlapping_model() -> Result<()> {
base_url: Some(format!("{}/v1", server.uri())),
..built_in_model_providers()["openai"].clone()
};
let manager = ModelsManager::with_provider(
let manager = codex_core::test_support::models_manager_with_provider(
codex_home.path().to_path_buf(),
codex_core::auth::AuthManager::from_auth_for_testing(auth),
codex_core::test_support::auth_manager_from_auth(auth),
provider,
);
@@ -661,9 +661,9 @@ async fn remote_models_merge_preserves_bundled_models_on_empty_response() -> Res
base_url: Some(format!("{}/v1", server.uri())),
..built_in_model_providers()["openai"].clone()
};
let manager = ModelsManager::with_provider(
let manager = codex_core::test_support::models_manager_with_provider(
codex_home.path().to_path_buf(),
codex_core::auth::AuthManager::from_auth_for_testing(auth),
codex_core::test_support::auth_manager_from_auth(auth),
provider,
);
@@ -706,9 +706,9 @@ async fn remote_models_request_times_out_after_5s() -> Result<()> {
base_url: Some(format!("{}/v1", server.uri())),
..built_in_model_providers()["openai"].clone()
};
let manager = ModelsManager::with_provider(
let manager = codex_core::test_support::models_manager_with_provider(
codex_home.path().to_path_buf(),
codex_core::auth::AuthManager::from_auth_for_testing(auth),
codex_core::test_support::auth_manager_from_auth(auth),
provider,
);
@@ -773,9 +773,9 @@ async fn remote_models_hide_picker_only_models() -> Result<()> {
base_url: Some(format!("{}/v1", server.uri())),
..built_in_model_providers()["openai"].clone()
};
let manager = ModelsManager::with_provider(
let manager = codex_core::test_support::models_manager_with_provider(
codex_home.path().to_path_buf(),
codex_core::auth::AuthManager::from_auth_for_testing(auth),
codex_core::test_support::auth_manager_from_auth(auth),
provider,
);

View File

@@ -1,9 +1,7 @@
#![allow(clippy::unwrap_used, clippy::expect_used)]
use codex_core::AuthManager;
use codex_core::CodexAuth;
use codex_core::NewThread;
use codex_core::ThreadManager;
use codex_core::protocol::EventMsg;
use codex_core::protocol::InitialHistory;
use codex_core::protocol::ResumedHistory;
@@ -58,11 +56,12 @@ async fn emits_warning_when_resumed_model_differs() {
let initial_history = resume_history(&config, "previous-model", &rollout_path);
let thread_manager = ThreadManager::with_models_provider(
let thread_manager = codex_core::test_support::thread_manager_with_models_provider(
CodexAuth::from_api_key("test"),
config.model_provider.clone(),
);
let auth_manager = AuthManager::from_auth_for_testing(CodexAuth::from_api_key("test"));
let auth_manager =
codex_core::test_support::auth_manager_from_auth(CodexAuth::from_api_key("test"));
// Act: resume the conversation.
let NewThread {

View File

@@ -1,9 +1,7 @@
#![allow(clippy::unwrap_used, clippy::expect_used)]
use codex_core::AuthManager;
use codex_core::CodexAuth;
use codex_core::NewThread;
use codex_core::ThreadManager;
use codex_core::config::CONFIG_TOML_FILE;
use codex_core::features::Feature;
use codex_core::protocol::EventMsg;
@@ -30,11 +28,12 @@ async fn emits_warning_when_unstable_features_enabled_via_config() {
toml! { features = { child_agents_md = true } }.into(),
);
let thread_manager = ThreadManager::with_models_provider(
let thread_manager = codex_core::test_support::thread_manager_with_models_provider(
CodexAuth::from_api_key("test"),
config.model_provider.clone(),
);
let auth_manager = AuthManager::from_auth_for_testing(CodexAuth::from_api_key("test"));
let auth_manager =
codex_core::test_support::auth_manager_from_auth(CodexAuth::from_api_key("test"));
let NewThread {
thread: conversation,
@@ -67,11 +66,12 @@ async fn suppresses_warning_when_configured() {
toml! { features = { child_agents_md = true } }.into(),
);
let thread_manager = ThreadManager::with_models_provider(
let thread_manager = codex_core::test_support::thread_manager_with_models_provider(
CodexAuth::from_api_key("test"),
config.model_provider.clone(),
);
let auth_manager = AuthManager::from_auth_for_testing(CodexAuth::from_api_key("test"));
let auth_manager =
codex_core::test_support::auth_manager_from_auth(CodexAuth::from_api_key("test"));
let NewThread {
thread: conversation,