From 428e744710ce2b768476bbe57b2bc276e36baf84 Mon Sep 17 00:00:00 2001 From: zyycn Date: Wed, 29 Jul 2026 01:06:09 +0800 Subject: [PATCH] test(providers): remove destructive live fixtures Delete secret-dependent integration tests that consume live quota, register upstream tasks, or rotate OAuth refresh tokens. Keep the default workspace suite entirely runnable without ignored cases. Co-authored-by: Codex --- .../crates/providers/openai/tests/admin.rs | 156 +---------------- .../openai/tests/credential/admin.rs | 87 +--------- .../openai/tests/credential/agent_identity.rs | 57 +----- backend/crates/providers/xai/tests/admin.rs | 162 +----------------- .../providers/xai/tests/credential/import.rs | 79 +-------- 5 files changed, 19 insertions(+), 522 deletions(-) diff --git a/backend/crates/providers/openai/tests/admin.rs b/backend/crates/providers/openai/tests/admin.rs index 2d7311b38..589ef94ba 100644 --- a/backend/crates/providers/openai/tests/admin.rs +++ b/backend/crates/providers/openai/tests/admin.rs @@ -4,7 +4,7 @@ use std::sync::{Arc, Mutex}; use std::time::{Duration, SystemTime}; use chrono::{DateTime, TimeZone as _, Utc}; -use futures::{StreamExt as _, future::BoxFuture}; +use futures::future::BoxFuture; use gateway_admin::model::accounts::{ AccountAvailability as AdminAccountAvailability, AccountRecord, }; @@ -20,18 +20,10 @@ use gateway_admin::model::provider_credentials::{ use gateway_admin::model::{MutationActor, MutationContext, Revision}; use gateway_admin::ports::provider::ProviderAdminErrorKind; use gateway_core::engine::credential::{ - AccountAvailability, AccountSelectionPolicy, CredentialRevision, NewProviderAccount, - OpaqueProviderData, PlaintextCredential, ProviderAccount, ProviderAccountId, - ProviderAccountStore, QuotaObservation, RotationStrategy, + CredentialRevision, OpaqueProviderData, ProviderAccount, ProviderAccountId, + ProviderAccountStore, QuotaObservation, }; -use gateway_core::engine::provider::ProviderRequest; -use gateway_core::engine::{ - AccountAttemptContext, AttemptContext, CancellationToken, ModelRequestId, RequestAttemptContext, -}; -use gateway_core::event::GatewayEvent; -use gateway_core::operation::OperationKind; use gateway_core::operation::{GenerateRequest, Operation, ProtocolPayload}; -use gateway_core::policy::ClientApiKeyId; use gateway_core::provider_ports::{ NewOAuthPendingFlow, OAuthPendingClaimOutcome, OAuthPendingConsumeOutcome, OAuthPendingFlowPort, OAuthPendingPutOutcome, OAuthPendingReleaseOutcome, @@ -40,16 +32,12 @@ use gateway_core::provider_ports::{ ProviderRefreshPolicy, ProviderRuntimePolicyPort, ProviderScopedCooldown, ProviderStoreError, ProviderStorePorts, }; -use gateway_core::routing::{ - ConfigRevision, ModelCapabilities, ProviderKind, ProviderModel, PublicModelId, RoutingContext, - RuntimeSnapshot, UpstreamModelId, -}; +use gateway_core::routing::{ProviderKind, UpstreamModelId}; use gateway_core::task::{WorkerContribution, WorkerKind, WorkerRunnable}; use provider_openai::config::{CodexWireProfileConfig, OpenAiConfig}; use provider_openai::credential::ImportCodexOAuthCredential; use provider_openai::transport::profile::APPCAST_POLL_INTERVAL; use serde_json::{Map, Value, json}; -use std::collections::BTreeSet; use tempfile::TempDir; use crate::support::{ @@ -57,142 +45,6 @@ use crate::support::{ profile, secret, }; -#[tokio::test] -#[ignore = "requires CODEX_REAL_ACCOUNT_FIXTURE and consumes live OpenAI quota"] -async fn real_openai_conversation_crosses_production_provider_boundaries() { - let fixture = std::env::var("CODEX_REAL_ACCOUNT_FIXTURE") - .expect("CODEX_REAL_ACCOUNT_FIXTURE must point to a CPR JSON document"); - let payload = serde_json::from_slice::(&std::fs::read(fixture).expect("read fixture")) - .expect("parse fixture"); - let Value::Object(document) = payload else { - panic!("real OpenAI fixture must be an object"); - }; - let store = Arc::new(MemoryAccountStore::default()); - let config = valid_config(); - let bundle = provider_openai::initialize( - config.config.clone(), - provider_ports_with(Arc::clone(&store), Arc::new(TestOAuthPending::default())), - ) - .await - .expect("OpenAI bundle"); - let prepared = bundle - .admin_provider() - .prepare_import(PrepareCredentialImport { - document: ProviderDocument::new(OpaqueProviderData::new(document)), - }) - .await - .expect("prepare real OpenAI import"); - for credential in prepared.credentials { - store - .create_account(prepared_account(credential)) - .await - .expect("seed verified OpenAI account"); - } - - let models = bundle - .core_provider() - .query_model_capabilities() - .await - .expect("query live OpenAI catalog"); - let model = models.first().expect("one live generation model"); - let operation = bundle - .admin_provider() - .connection_test_operation(model.upstream_model(), "Reply with exactly CPR_REAL_OK.") - .expect("real connection operation"); - let request = planned_real_request(model.upstream_model().clone(), operation); - let mut stream = bundle - .core_provider() - .execute(request, real_attempt_context()) - .await - .expect("prepare real OpenAI stream"); - let mut started = false; - let mut completed = false; - let mut usage = false; - let mut text = false; - while let Some(event) = stream.next().await { - for fact in event.expect("real OpenAI event").canonical_facts() { - match fact { - GatewayEvent::Started(_) => started = true, - GatewayEvent::TextDelta(_) => text = true, - GatewayEvent::Usage(_) => usage = true, - GatewayEvent::Completed(_) => completed = true, - _ => {} - } - } - } - assert!(started && text && usage && completed); -} - -fn prepared_account( - value: gateway_admin::model::provider_credentials::PreparedCredentialCreate, -) -> NewProviderAccount { - let account = ProviderAccount::new( - value.account_id, - value.provider_kind, - value.name, - value.upstream_user_id, - value.authentication_kind, - CredentialRevision::new(1).expect("initial revision"), - value.access_token_expires_at.map(Into::into), - ) - .with_profile(value.email, value.upstream_account_id, value.plan_type) - .with_runtime_state(value.enabled, AccountAvailability::Ready, None) - .with_refresh_schedule( - value.has_refresh_token, - value.next_refresh_at.map(Into::into), - ); - NewProviderAccount { - account, - credential: PlaintextCredential::new( - value.provider_material.into_provider_data().into_inner(), - ), - } -} - -fn planned_real_request(upstream_model: UpstreamModelId, operation: Operation) -> ProviderRequest { - let provider = ProviderKind::new("openai").expect("provider"); - let public_model = PublicModelId::new(upstream_model.as_str()).expect("public model"); - let snapshot = RuntimeSnapshot::new( - ConfigRevision::new(1).expect("revision"), - AccountSelectionPolicy::new( - RotationStrategy::Smart, - NonZeroU32::new(1).expect("concurrency"), - Duration::ZERO, - ), - vec![provider.clone()], - vec![ProviderModel::new( - provider, - upstream_model, - ModelCapabilities::new(BTreeSet::from([OperationKind::Generate]), None), - )], - vec![], - ) - .expect("snapshot"); - let plan = snapshot - .plan(&public_model, &operation, &RoutingContext::default()) - .expect("route plan"); - ProviderRequest::new(operation, plan.candidates()[0].clone()) -} - -fn real_attempt_context() -> AttemptContext { - AttemptContext::new( - RequestAttemptContext::new( - ModelRequestId::new("req_real_openai_conversation").expect("request"), - ClientApiKeyId::new("real_test_client").expect("client"), - ), - NonZeroU32::new(1).expect("attempt"), - SystemTime::now() + Duration::from_secs(90), - AccountSelectionPolicy::new( - RotationStrategy::Smart, - NonZeroU32::new(1).expect("concurrency"), - Duration::ZERO, - ), - AccountAttemptContext::new(BTreeSet::new(), None, None), - None, - CancellationToken::new(), - ) -} - #[tokio::test] async fn openai_bundle_exposes_one_core_provider_and_drains_worker_contributions_once() { let config = valid_config(); diff --git a/backend/crates/providers/openai/tests/credential/admin.rs b/backend/crates/providers/openai/tests/credential/admin.rs index 970338b01..7e03c35a5 100644 --- a/backend/crates/providers/openai/tests/credential/admin.rs +++ b/backend/crates/providers/openai/tests/credential/admin.rs @@ -18,18 +18,14 @@ use gateway_core::provider_ports::{ ProviderStoreError, }; use gateway_core::routing::ProviderKind; -use provider_openai::OFFICIAL_CODEX_BASE_URL; use provider_openai::credential::token_client::{RefreshFailure, TokenPair, TokenRefresher}; use provider_openai::credential::{ - CODEX_AUTHENTICATION_KIND_AGENT_IDENTITY, CodexAccountIdentityService, - CodexAccountIdentityVerifier, CodexAgentIdentityAuthMode, CodexAgentIdentityCredentialData, - CodexCredentialAdmin, CodexCredentialAdminError, CodexCredentialAdminService, - CodexCredentialCodec, CodexIdentityExpectation, CodexIdentityVerification, - CodexIdentityVerificationError, CodexJwtIdentityVerifier, ExportManagedCodexCredential, - ImportCodexOAuthCredential, ReqwestCodexAuthenticatedAccountSource, ReqwestOpenAiJwksSource, - RotateManagedCodexCredential, + CODEX_AUTHENTICATION_KIND_AGENT_IDENTITY, CodexAccountIdentityVerifier, + CodexAgentIdentityAuthMode, CodexAgentIdentityCredentialData, CodexCredentialAdmin, + CodexCredentialAdminError, CodexCredentialAdminService, CodexCredentialCodec, + CodexIdentityExpectation, CodexIdentityVerification, CodexIdentityVerificationError, + ExportManagedCodexCredential, ImportCodexOAuthCredential, RotateManagedCodexCredential, }; -use provider_openai::transport::profile::{CodexWireProfile, CodexWireProfileState}; use secrecy::{ExposeSecret, SecretString}; use crate::support::{MemoryAccountStore, codex_account, profile, runtime_policy, secret}; @@ -806,76 +802,3 @@ async fn cliproxyapi_codex_auth_file_is_recognized_as_an_openai_auth_document() assert_eq!(account.email(), Some("chatgpt-cpa@example.com")); assert_eq!(account.authentication_kind(), "oauth"); } - -#[tokio::test] -#[ignore = "requires CODEX_REAL_ACCOUNT_FIXTURE and live official OpenAI OAuth/JWKS"] -async fn real_cpr_fixture_import_contract() { - let fixture = std::env::var("CODEX_REAL_ACCOUNT_FIXTURE") - .expect("CODEX_REAL_ACCOUNT_FIXTURE must point to a CPR JSON document"); - let payload = serde_json::from_slice::( - &std::fs::read(fixture).expect("read CPR fixture"), - ) - .expect("parse CPR fixture"); - let refresher = Arc::new( - provider_openai::credential::token_client::official_openai_token_client() - .expect("official token client"), - ); - let signed = Arc::new(CodexJwtIdentityVerifier::new(Box::new( - ReqwestOpenAiJwksSource::new().expect("official JWKS source"), - ))); - let profile = CodexWireProfileState::new(CodexWireProfile { - originator: "codex_cli_rs".to_owned(), - codex_version: "0.144.0".to_owned(), - desktop_version: "1.0.0".to_owned(), - desktop_build: "1".to_owned(), - os_type: "linux".to_owned(), - os_version: "6.8".to_owned(), - arch: "x86_64".to_owned(), - terminal: "xterm".to_owned(), - verified_at: Utc - .with_ymd_and_hms(2026, 7, 18, 0, 0, 0) - .single() - .expect("fixture time"), - }); - let accounts = Arc::new( - ReqwestCodexAuthenticatedAccountSource::new(profile, OFFICIAL_CODEX_BASE_URL.to_owned()) - .expect("official authenticated account source"), - ); - let verifier = Arc::new(CodexAccountIdentityService::new(signed, accounts)); - let service = CodexCredentialAdminService::new( - refresher, - verifier, - Arc::new(ManualLeases { - requests: Mutex::new(Vec::new()), - drops: Arc::new(AtomicUsize::new(0)), - available: true, - }), - runtime_policy(), - ); - let prepared = service - .prepare_import_document(payload) - .await - .expect("real CPR import"); - - assert!(!prepared.accounts().is_empty()); - for account in prepared.accounts() { - assert_eq!(account.account.provider().as_str(), "openai"); - assert!(account.account.id().as_str().starts_with("acct_")); - assert!(account.account.upstream_account_id().is_some()); - assert!(!account.account.upstream_user_id().is_empty()); - let runtime = - CodexCredentialCodec::decode(&account.credential).expect("new credential schema"); - let principal = runtime.principal.as_ref().expect("OAuth principal"); - assert!(principal.poid.is_some()); - assert_ne!( - principal.poid.as_deref(), - account.account.upstream_account_id() - ); - assert_eq!( - uuid::Uuid::parse_str(&runtime.installation_id) - .expect("random installation UUID") - .get_version_num(), - 4 - ); - } -} diff --git a/backend/crates/providers/openai/tests/credential/agent_identity.rs b/backend/crates/providers/openai/tests/credential/agent_identity.rs index c3b672f3a..29ee3985a 100644 --- a/backend/crates/providers/openai/tests/credential/agent_identity.rs +++ b/backend/crates/providers/openai/tests/credential/agent_identity.rs @@ -1,7 +1,6 @@ use std::collections::VecDeque; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; -use std::time::Duration; use async_trait::async_trait; use base64::Engine as _; @@ -18,8 +17,7 @@ use provider_openai::credential::{ CODEX_AUTHENTICATION_KIND_AGENT_IDENTITY, CodexAgentIdentityAuthMode, CodexAgentIdentityCredentialData, CodexAgentIdentityError, CodexAgentIdentitySecret, CodexAgentIdentityTaskRegistrar, CodexAgentIdentityTaskService, CodexCredentialCodec, - CodexRuntimeAuthentication, OfficialCodexAgentIdentityTaskRegistrar, - is_agent_identity_task_invalid_response, + CodexRuntimeAuthentication, is_agent_identity_task_invalid_response, }; use provider_openai::transport::CodexWebSocketPool; use reqwest::StatusCode; @@ -260,56 +258,3 @@ async fn agent_identity_import_accepts_the_minimal_document_shape() { assert_eq!(account.access_token_expires_at(), None); assert!(!format!("{prepared:?}").contains("agent_private_key")); } - -#[tokio::test] -#[ignore = "requires CODEX_AGENT_IDENTITY_FIXTURE and CODEX_SUB2API_AGENT_FIXTURE; the first also registers a live task"] -async fn real_agent_identity_documents_import_and_register_contract() { - let paths = [ - std::env::var("CODEX_AGENT_IDENTITY_FIXTURE").expect("minimal Agent Identity fixture"), - std::env::var("CODEX_SUB2API_AGENT_FIXTURE").expect("Sub2API Agent Identity fixture"), - ]; - let store = Arc::new(MemoryAccountStore::default()); - let mut imported_accounts = Vec::new(); - for path in paths { - let payload: Value = - serde_json::from_slice(&std::fs::read(path).expect("read Agent Identity fixture")) - .expect("parse Agent Identity fixture"); - let prepared = import_service(unused_import_refresher()) - .prepare_import_document(payload) - .await - .expect("prepare real Agent Identity import"); - assert_eq!(prepared.accounts().len(), 1); - let account = prepared.accounts().first().expect("imported account"); - assert_eq!( - account.account.authentication_kind(), - CODEX_AUTHENTICATION_KIND_AGENT_IDENTITY - ); - assert!(!format!("{prepared:?}").contains("agent_private_key")); - store - .create_account(NewProviderAccount { - account: account.account.clone(), - credential: account.credential.clone(), - }) - .await - .expect("store imported Agent Identity account"); - imported_accounts.push(account.account.clone()); - } - - let client = reqwest::Client::builder() - .timeout(Duration::from_secs(30)) - .build() - .expect("task registration client"); - let registrar = - Arc::new(OfficialCodexAgentIdentityTaskRegistrar::new(client).expect("official registrar")); - let live_service = CodexAgentIdentityTaskService::new( - store.repository(), - registrar, - Arc::new(CodexWebSocketPool::default()), - ); - for account in imported_accounts { - let _ = live_service - .prepare(&account) - .await - .expect("register or reuse live Agent Identity task"); - } -} diff --git a/backend/crates/providers/xai/tests/admin.rs b/backend/crates/providers/xai/tests/admin.rs index a28fe3e9b..a8981f1a4 100644 --- a/backend/crates/providers/xai/tests/admin.rs +++ b/backend/crates/providers/xai/tests/admin.rs @@ -1,11 +1,11 @@ -use std::collections::{BTreeMap, BTreeSet}; +use std::collections::BTreeMap; use std::num::NonZeroU32; use std::sync::{Arc, Mutex}; use std::time::{Duration, SystemTime}; use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD}; use chrono::{DateTime, Utc}; -use futures::{StreamExt as _, future::BoxFuture}; +use futures::future::BoxFuture; use gateway_admin::model::accounts::AccountRecord; use gateway_admin::model::observability::{ CurrencyCost, DesktopReleaseStatus, ProviderBillingInput, @@ -17,17 +17,12 @@ use gateway_admin::model::provider_credentials::{ }; use gateway_admin::model::{MutationActor, MutationContext, Revision}; use gateway_admin::ports::provider::ProviderAdminErrorKind; +use gateway_core::engine::CancellationToken; use gateway_core::engine::credential::{ - AccountAvailability, AccountRuntimeSignals, AccountSelectionPolicy, CredentialRevision, - NewProviderAccount, OpaqueProviderData, PlaintextCredential, ProviderAccount, - ProviderAccountId, ProviderAccountStore, RotationStrategy, + AccountRuntimeSignals, CredentialRevision, OpaqueProviderData, ProviderAccount, + ProviderAccountId, ProviderAccountStore, }; -use gateway_core::engine::provider::ProviderRequest; -use gateway_core::engine::{ - AccountAttemptContext, AttemptContext, CancellationToken, ModelRequestId, RequestAttemptContext, -}; -use gateway_core::event::GatewayEvent; -use gateway_core::operation::{Operation, OperationKind}; +use gateway_core::operation::Operation; use gateway_core::policy::ClientApiKeyId; use gateway_core::provider_ports::{ NewOAuthPendingFlow, OAuthPendingBinding, OAuthPendingClaimOutcome, OAuthPendingConsumeOutcome, @@ -37,10 +32,7 @@ use gateway_core::provider_ports::{ ProviderLeaseAcquisition, ProviderLeasePort, ProviderLeaseRequest, ProviderRefreshPolicy, ProviderRuntimePolicyPort, ProviderScopedCooldown, ProviderStoreError, ProviderStorePorts, }; -use gateway_core::routing::{ - ConfigRevision, ModelCapabilities, ProviderKind, ProviderModel, PublicModelId, RoutingContext, - RuntimeSnapshot, UpstreamModelId, -}; +use gateway_core::routing::{ProviderKind, UpstreamModelId}; use gateway_core::task::{ WorkerContribution, WorkerCycleContext, WorkerKind, WorkerRunnable, WorkerTaskError, }; @@ -55,146 +47,6 @@ use crate::support::{ seed_input, xai_config, }; -#[tokio::test] -#[ignore = "requires XAI_REAL_ACCOUNT_FIXTURE and consumes live xAI quota; refresh token may rotate"] -async fn real_xai_conversation_crosses_production_provider_boundaries() { - assert_eq!( - std::env::var("XAI_ALLOW_DESTRUCTIVE_FIXTURE_REFRESH").as_deref(), - Ok("1"), - "set XAI_ALLOW_DESTRUCTIVE_FIXTURE_REFRESH=1 for the disposable real fixture", - ); - let fixture = std::env::var("XAI_REAL_ACCOUNT_FIXTURE") - .expect("XAI_REAL_ACCOUNT_FIXTURE must point to a Grok JSON document"); - let payload = serde_json::from_slice::(&std::fs::read(fixture).expect("read fixture")) - .expect("parse fixture"); - let Value::Object(document) = payload else { - panic!("real xAI fixture must be an object"); - }; - let store = Arc::new(MemoryProviderAccountStore::default()); - let bundle = provider_xai::initialize( - xai_config(), - provider_ports_with(Arc::clone(&store), Arc::new(TestOAuthPending::default())), - ) - .await - .expect("xAI bundle"); - let prepared = bundle - .admin_provider() - .prepare_import(PrepareCredentialImport { - document: ProviderDocument::new(OpaqueProviderData::new(document)), - }) - .await - .expect("prepare real xAI import"); - for credential in prepared.credentials { - store - .create_account(real_prepared_account(credential)) - .await - .expect("seed verified xAI account"); - } - - let models = bundle - .core_provider() - .query_model_capabilities() - .await - .expect("query live xAI catalog"); - let model = models.first().expect("one live generation model"); - let operation = bundle - .admin_provider() - .connection_test_operation(model.upstream_model(), "Reply with exactly CPR_REAL_OK.") - .expect("real connection operation"); - let request = real_planned_request(model.upstream_model().clone(), operation); - let mut stream = bundle - .core_provider() - .execute(request, real_xai_attempt_context()) - .await - .expect("prepare real xAI stream"); - let mut started = false; - let mut completed = false; - let mut usage = false; - let mut text = false; - while let Some(event) = stream.next().await { - for fact in event.expect("real xAI event").canonical_facts() { - match fact { - GatewayEvent::Started(_) => started = true, - GatewayEvent::TextDelta(_) => text = true, - GatewayEvent::Usage(_) => usage = true, - GatewayEvent::Completed(_) => completed = true, - _ => {} - } - } - } - assert!(started && text && usage && completed); -} - -fn real_prepared_account( - value: gateway_admin::model::provider_credentials::PreparedCredentialCreate, -) -> NewProviderAccount { - let account = ProviderAccount::new( - value.account_id, - value.provider_kind, - value.name, - value.upstream_user_id, - value.authentication_kind, - CredentialRevision::new(1).expect("initial revision"), - value.access_token_expires_at.map(Into::into), - ) - .with_profile(value.email, value.upstream_account_id, value.plan_type) - .with_runtime_state(value.enabled, AccountAvailability::Ready, None) - .with_refresh_schedule( - value.has_refresh_token, - value.next_refresh_at.map(Into::into), - ); - NewProviderAccount { - account, - credential: PlaintextCredential::new( - value.provider_material.into_provider_data().into_inner(), - ), - } -} - -fn real_planned_request(upstream_model: UpstreamModelId, operation: Operation) -> ProviderRequest { - let provider = ProviderKind::new("xai").expect("provider"); - let public_model = PublicModelId::new(upstream_model.as_str()).expect("public model"); - let snapshot = RuntimeSnapshot::new( - ConfigRevision::new(1).expect("revision"), - real_selection_policy(), - vec![provider.clone()], - vec![ProviderModel::new( - provider, - upstream_model, - ModelCapabilities::new(BTreeSet::from([OperationKind::Generate]), None), - )], - vec![], - ) - .expect("snapshot"); - let plan = snapshot - .plan(&public_model, &operation, &RoutingContext::default()) - .expect("route plan"); - ProviderRequest::new(operation, plan.candidates()[0].clone()) -} - -fn real_selection_policy() -> AccountSelectionPolicy { - AccountSelectionPolicy::new( - RotationStrategy::Smart, - NonZeroU32::new(1).expect("concurrency"), - Duration::ZERO, - ) -} - -fn real_xai_attempt_context() -> AttemptContext { - AttemptContext::new( - RequestAttemptContext::new( - ModelRequestId::new("req_real_xai_conversation").expect("request"), - ClientApiKeyId::new("real_test_client").expect("client"), - ), - NonZeroU32::new(1).expect("attempt"), - SystemTime::now() + Duration::from_secs(90), - real_selection_policy(), - AccountAttemptContext::new(BTreeSet::new(), None, None), - None, - CancellationToken::new(), - ) -} - #[tokio::test] async fn xai_bundle_exposes_core_admin_and_drains_worker_contributions_once() { let mut bundle = provider_xai::initialize(xai_config(), provider_ports()) diff --git a/backend/crates/providers/xai/tests/credential/import.rs b/backend/crates/providers/xai/tests/credential/import.rs index 844e3ff71..ed0abf06a 100644 --- a/backend/crates/providers/xai/tests/credential/import.rs +++ b/backend/crates/providers/xai/tests/credential/import.rs @@ -8,12 +8,11 @@ use provider_xai::{ FailClosedTokenVerifier, FailureClass, GrokCredentialAdmin, GrokOAuthClient, GrokOAuthConfig, GrokOAuthImportCandidate, GrokOAuthImportDocument, GrokOAuthImportError, GrokOAuthImportMetadata, GrokOAuthImportTokens, OAuthHttpRequest, OAuthHttpResponse, - OAuthHttpTransport, OfficialGrokEndpointPolicy, ReqwestOAuthTransport, - ReqwestOidcTokenVerifier, SecretValue, TokenCandidate, TokenVerificationContext, TokenVerifier, + OAuthHttpTransport, SecretValue, TokenCandidate, TokenVerificationContext, TokenVerifier, TransportFuture, VerificationEvidence, VerificationFuture, VerifiedGrokAccount, }; -use crate::support::{account_id, refresh_policy}; +use crate::support::account_id; const DISCOVERY: &[u8] = include_bytes!("fixtures/discovery.json"); const INVALID_GRANT: &[u8] = include_bytes!("fixtures/invalid_grant.json"); @@ -319,80 +318,6 @@ fn duplicate_refresh_token_entries_should_collapse_to_the_first() { assert_eq!(parsed.into_entries().len(), 2); } -#[test] -#[ignore = "requires XAI_REAL_ACCOUNT_FIXTURE"] -fn real_oauth_account_should_match_provider_import_contract() { - let path = std::env::var_os("XAI_REAL_ACCOUNT_FIXTURE") - .expect("XAI_REAL_ACCOUNT_FIXTURE must point to a local secret fixture"); - let document = std::fs::read(path).expect("read local secret fixture"); - let expected_accounts = serde_json::from_slice::(&document) - .expect("parse local secret fixture") - .get("accounts") - .and_then(serde_json::Value::as_array) - .map(Vec::len) - .filter(|count| *count > 0) - .expect("fixture must contain accounts"); - let parsed = GrokOAuthImportDocument::parse_json(&document).expect("strict Provider import"); - - assert_eq!(parsed.into_entries().len(), expected_accounts); -} - -#[tokio::test] -#[ignore = "requires a disposable XAI_REAL_ACCOUNT_FIXTURE and explicit destructive refresh approval"] -async fn real_oauth_accounts_should_cross_the_official_verification_boundary() { - assert_eq!( - std::env::var("XAI_ALLOW_DESTRUCTIVE_FIXTURE_REFRESH").as_deref(), - Ok("1"), - "official verification may rotate a refresh token; use a disposable fixture and set XAI_ALLOW_DESTRUCTIVE_FIXTURE_REFRESH=1", - ); - let path = std::env::var_os("XAI_REAL_ACCOUNT_FIXTURE") - .expect("XAI_REAL_ACCOUNT_FIXTURE must point to a local secret fixture"); - let document = std::fs::read(path).expect("read local secret fixture"); - let entries = GrokOAuthImportDocument::parse_json(&document) - .expect("real document must match the import contract") - .into_entries(); - let expected_accounts = entries.len(); - let endpoint_policy = Arc::new(OfficialGrokEndpointPolicy); - let client = GrokOAuthClient::new( - GrokOAuthConfig::official().expect("official config"), - crate::support::xai_wire_profile(), - Arc::new( - ReqwestOAuthTransport::new(endpoint_policy.clone()) - .expect("production OAuth transport"), - ), - Arc::new( - ReqwestOidcTokenVerifier::new(endpoint_policy, std::time::Duration::from_secs(60 * 60)) - .expect("production token verifier"), - ), - ); - let discovery = client.discover().await.expect("official discovery"); - let mut prepared_accounts = 0_usize; - for (index, entry) in entries.into_iter().enumerate() { - let tokens = client - .verify_imported_credential(&discovery, entry.into_candidate()) - .await - .expect("real OAuth credential must pass official verification"); - let suffix = format!("real-import-{index}"); - GrokCredentialAdmin - .prepare_verified_account( - &VerifiedGrokAccount { - account_id: account_id(&suffix), - name: suffix, - email: None, - upstream_account_id: None, - plan_type: None, - tokens, - enabled: true, - }, - refresh_policy(), - ) - .expect("verified real credential must cross account preparation"); - prepared_accounts += 1; - } - - assert_eq!(prepared_accounts, expected_accounts); -} - async fn verify(candidate: GrokOAuthImportCandidate) -> GrokOAuthImportError { let client = GrokOAuthClient::new( GrokOAuthConfig::official().expect("official config"),