Honor the configured credential store in gateway test fixtures (#59495)

## What changed

Update `seed_gateway_auth` to seed either the keyring or file credential store according to the provider's configuration, using `GatewayAuthManager::credential_id()` to identify credentials.

Use file storage in the gateway model-cache reuse and login-required invalidation tests to keep keyring latency outside the behavior under test.

GitOrigin-RevId: 23b4f31b6d9f9561638267723582af73eabefcfc
This commit is contained in:
jif
2026-11-04 21:22:28 +00:00
committed by copyberry
parent 5112ce6ede
commit 464239de0a
2 changed files with 44 additions and 35 deletions
@@ -1,4 +1,4 @@
//! Seeds encrypted gateway credentials for provider and agent integration tests.
//! Seeds gateway credentials in the configured store for provider and agent integration tests.
#![allow(clippy::expect_used)] // Fixture setup failures should fail the calling test immediately.
@@ -11,13 +11,12 @@ use codex_login::AuthManager;
use codex_login::GatewayAuthConfig;
use codex_login::GatewayAuthManager;
use codex_model_provider_info::ModelProviderInfo;
use codex_model_provider_info::OAuthCredentialsStore;
use codex_secrets::LocalSecretsNamespace;
use codex_secrets::SecretName;
use codex_secrets::SecretScope;
use codex_secrets::SecretsBackendKind;
use codex_secrets::SecretsManager;
use sha2::Digest;
use sha2::Sha256;
use super::process_shared_state;
@@ -59,7 +58,7 @@ impl KeyringStore for TestKeyring {
}
}
/// Seeds a provider's encrypted gateway store with a test-only keyring passphrase.
/// Seeds a provider's configured gateway store, using a test passphrase for the keyring backend.
/// Keep the returned manager alive while requests use this fixture.
pub fn seed_gateway_auth(
info: &ModelProviderInfo,
@@ -85,43 +84,51 @@ pub fn seed_gateway_auth(
let keyring = Arc::new(TestKeyring(Mutex::new(Some(
"gateway-test-passphrase".to_string(),
))));
let secrets = SecretsManager::new_with_keyring_store_and_namespace(
runtime.codex_home.clone(),
SecretsBackendKind::Local,
keyring.clone(),
LocalSecretsNamespace::GatewayOAuth,
);
// Match the credential identity used by GatewayAuthManager's encrypted store.
let mut digest = Sha256::new();
digest.update(runtime.codex_home.to_string_lossy().as_bytes());
digest.update([0]);
for value in [
oauth.authorization_url.as_str(),
oauth.token_url.as_str(),
oauth.client_id.as_str(),
oauth.resource.as_deref().unwrap_or_default(),
] {
digest.update(value.as_bytes());
digest.update([0]);
}
for scope in &oauth.scopes {
digest.update(scope.as_bytes());
digest.update([0]);
}
let name = SecretName::new(&format!("PROVIDER_OAUTH_{:X}", digest.finalize()))
.expect("gateway test credential name");
secrets
.set(&SecretScope::Global, &name, &token.to_string())
.expect("seed encrypted gateway credentials");
let manager = Arc::new(
GatewayAuthManager::new(
oauth.clone(),
runtime.codex_home.clone(),
runtime.auth_route_config.http_client_factory(),
keyring,
keyring.clone(),
)
.expect("gateway test HTTP client"),
);
let credential_id = manager.credential_id();
match oauth.credentials_store {
OAuthCredentialsStore::Keyring => {
let secrets = SecretsManager::new_with_keyring_store_and_namespace(
runtime.codex_home.clone(),
SecretsBackendKind::Local,
keyring,
LocalSecretsNamespace::GatewayOAuth,
);
let digest = credential_id
.strip_prefix("provider-oauth|")
.expect("gateway credential ID");
let name = SecretName::new(&format!("PROVIDER_OAUTH_{}", digest.to_ascii_uppercase()))
.expect("gateway test credential name");
secrets
.set(&SecretScope::Global, &name, &token.to_string())
.expect("seed encrypted gateway credentials");
}
OAuthCredentialsStore::File => {
let dir = runtime.codex_home.join("secrets");
std::fs::create_dir_all(&dir).expect("create test credentials directory");
let file = dir.join("gateway_oauth.json");
let mut tokens: std::collections::BTreeMap<String, serde_json::Value> =
match std::fs::read(&file) {
Ok(bytes) => serde_json::from_slice(&bytes).expect("parse test gateway tokens"),
Err(err) if err.kind() == std::io::ErrorKind::NotFound => Default::default(),
Err(err) => panic!("read test gateway file: {err}"),
};
tokens.insert(credential_id, token);
std::fs::write(
file,
serde_json::to_vec(&tokens).expect("serialize test gateway tokens"),
)
.expect("seed test gateway file");
}
}
process_shared_state()
.gateway_managers
.lock()
@@ -20,6 +20,7 @@ use codex_login::default_client::ClientRedirectPolicy;
use codex_login::default_client::create_client_for_route;
use codex_model_provider_info::GatewayOAuthDelivery;
use codex_model_provider_info::ModelProviderInfo;
use codex_model_provider_info::OAuthCredentialsStore;
use codex_models_manager::manager::RefreshStrategy;
use codex_protocol::error::CodexErrorDetails;
use codex_protocol::protocol::CodexErrorInfo;
@@ -408,7 +409,8 @@ async fn models_cache_is_reused_only_for_matching_gateway_configuration() {
resource: None,
scopes: vec![],
redirect_port: None,
credentials_store: Default::default(),
// Keyring latency is outside the cache behavior under test.
credentials_store: OAuthCredentialsStore::File,
delivery: GatewayOAuthDelivery::Header {
name: "x-gateway-auth".into(),
scheme: "Bearer".into(),
@@ -672,7 +674,7 @@ async fn gateway_login_required_invalidates_the_last_successful_catalog() {
resource: None,
scopes: vec![],
redirect_port: None,
credentials_store: Default::default(),
credentials_store: OAuthCredentialsStore::File,
delivery: GatewayOAuthDelivery::Header {
name: "x-gateway-auth".into(),
scheme: "Bearer".into(),