mirror of
https://github.com/openai/codex.git
synced 2026-05-25 05:24:37 +00:00
[codex-core] add revoked auth integration coverage [ci changed_files]
This commit is contained in:
@@ -716,6 +716,35 @@ async fn token_invalidated_error_type_401_clears_auth_and_requires_relogin() {
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn token_revoked_error_code_401_clears_auth_and_requires_relogin() {
|
||||
let (_codex_home, manager) = managed_chatgpt_auth_manager("revoked-access-token").await;
|
||||
let mut recovery = Some(manager.unauthorized_recovery());
|
||||
|
||||
let err = handle_unauthorized(
|
||||
TransportError::Http {
|
||||
status: StatusCode::UNAUTHORIZED,
|
||||
url: Some("https://chatgpt.com/backend-api/codex/responses".to_string()),
|
||||
headers: None,
|
||||
body: Some(r#"{"error":{"code":"token_revoked"}}"#.to_string()),
|
||||
},
|
||||
&mut recovery,
|
||||
&test_session_telemetry(),
|
||||
)
|
||||
.await
|
||||
.expect_err("revoked access tokens should force relogin");
|
||||
|
||||
let CodexErr::RefreshTokenFailed(failed) = err else {
|
||||
panic!("expected revoked access token to force relogin, got {err:?}");
|
||||
};
|
||||
assert_eq!(failed.reason, RefreshTokenFailedReason::Revoked);
|
||||
assert_eq!(
|
||||
failed.message,
|
||||
"Your ChatGPT session is no longer valid. Please sign in again."
|
||||
);
|
||||
assert!(manager.auth_cached().is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn token_invalidated_401_retries_when_persisted_auth_changed() {
|
||||
let (codex_home, manager) = managed_chatgpt_auth_manager("revoked-access-token").await;
|
||||
|
||||
@@ -339,7 +339,7 @@ impl ThreadManager {
|
||||
state_db: Option<StateDbHandle>,
|
||||
) -> Self {
|
||||
set_thread_manager_test_mode_for_tests(/*enabled*/ true);
|
||||
let auth_manager = AuthManager::from_auth_for_testing(auth);
|
||||
let auth_manager = AuthManager::from_auth_for_testing_with_home(auth, codex_home.clone());
|
||||
let installation_id = uuid::Uuid::new_v4().to_string();
|
||||
let skills_codex_home = match AbsolutePathBuf::from_absolute_path_checked(&codex_home) {
|
||||
Ok(codex_home) => codex_home,
|
||||
|
||||
@@ -1066,6 +1066,95 @@ async fn chatgpt_auth_sends_correct_request() {
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn revoked_chatgpt_auth_user_turn_clears_auth_and_requests_relogin() -> anyhow::Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
|
||||
let server = MockServer::start().await;
|
||||
Mock::given(method("POST"))
|
||||
.and(path("/api/codex/responses"))
|
||||
.respond_with(ResponseTemplate::new(401).set_body_json(json!({
|
||||
"error": {
|
||||
"code": "token_revoked",
|
||||
"message": "revoked",
|
||||
}
|
||||
})))
|
||||
.expect(1)
|
||||
.mount(&server)
|
||||
.await;
|
||||
|
||||
let codex_home = Arc::new(TempDir::new()?);
|
||||
let _jwt = write_auth_json(
|
||||
codex_home.as_ref(),
|
||||
/*openai_api_key*/ None,
|
||||
"pro",
|
||||
"revoked-access-token",
|
||||
Some("account_id"),
|
||||
);
|
||||
let auth = CodexAuth::from_auth_storage(
|
||||
codex_home.path(),
|
||||
AuthCredentialsStoreMode::File,
|
||||
/*chatgpt_base_url*/ None,
|
||||
)
|
||||
.await?
|
||||
.expect("managed ChatGPT auth should load");
|
||||
|
||||
let mut model_provider = built_in_model_providers(/* openai_base_url */ None)["openai"].clone();
|
||||
model_provider.base_url = Some(format!("{}/api/codex", server.uri()));
|
||||
model_provider.supports_websockets = false;
|
||||
let mut builder = test_codex()
|
||||
.with_home(codex_home.clone())
|
||||
.with_auth(auth)
|
||||
.with_config(move |config| {
|
||||
config.model_provider = model_provider;
|
||||
});
|
||||
let test = builder.build(&server).await?;
|
||||
let codex = test.codex.clone();
|
||||
|
||||
codex
|
||||
.submit(Op::UserInput {
|
||||
environments: None,
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
responsesapi_client_metadata: None,
|
||||
})
|
||||
.await?;
|
||||
|
||||
let error_event = wait_for_event(&codex, |ev| matches!(ev, EventMsg::Error(_))).await;
|
||||
assert!(
|
||||
matches!(
|
||||
error_event,
|
||||
EventMsg::Error(ref err)
|
||||
if err.message.contains(
|
||||
"Your ChatGPT session is no longer valid. Please sign in again."
|
||||
)
|
||||
),
|
||||
"expected invalidated-session relogin error; got {error_event:?}"
|
||||
);
|
||||
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
|
||||
|
||||
assert!(
|
||||
!test.codex_home_path().join("auth.json").exists(),
|
||||
"revoked managed ChatGPT auth should be removed"
|
||||
);
|
||||
let response_attempts = server
|
||||
.received_requests()
|
||||
.await
|
||||
.expect("mock server should capture requests")
|
||||
.into_iter()
|
||||
.filter(|request| request.url.path() == "/api/codex/responses")
|
||||
.count();
|
||||
assert_eq!(
|
||||
response_attempts, 1,
|
||||
"revoked managed auth should fail without retrying /responses"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn prefers_apikey_when_config_prefers_apikey_even_with_chatgpt_tokens() {
|
||||
skip_if_no_network!();
|
||||
|
||||
Reference in New Issue
Block a user