diff --git a/crates/matrix-sdk/src/authentication/oauth/tests.rs b/crates/matrix-sdk/src/authentication/oauth/tests.rs index 9829b08a4..182b8d8d8 100644 --- a/crates/matrix-sdk/src/authentication/oauth/tests.rs +++ b/crates/matrix-sdk/src/authentication/oauth/tests.rs @@ -11,6 +11,7 @@ use ruma::{ }; use tokio::sync::broadcast::error::TryRecvError; use url::Url; +use wiremock::ResponseTemplate; use super::{ AuthorizationCode, AuthorizationError, AuthorizationResponse, OAuth, OAuthAuthorizationData, @@ -23,6 +24,8 @@ use crate::{ error::{AuthorizationCodeErrorResponseType, OAuthClientRegistrationError}, }, client::caches::CachedValue, + config::RequestConfig, + executor::spawn, test_utils::{ client::{ MockClientBuilder, mock_prev_session_tokens_with_refresh, @@ -713,6 +716,49 @@ async fn test_server_metadata_cache() { assert_matches!(client.inner.caches.server_metadata.value(), CachedValue::Cached(value) if !value.has_expired()); } +#[async_test] +async fn test_server_metadata_cache_refresh_lock() { + let server = MatrixMockServer::new().await; + + let oauth_server = server.oauth(); + // Make the first request to the endpoint fail after a second. + oauth_server + .mock_server_metadata() + .respond_with(ResponseTemplate::new(500).set_delay(Duration::from_secs(1))) + .mock_once() + .mount() + .await; + // The following request will succeed. + oauth_server.mock_server_metadata().ok().expect(1).mount().await; + + let client = server + .client_builder() + .logged_in_with_oauth() + // Disable retries so the first request succeeds or fails without waiting. + .on_builder(|builder| builder.request_config(RequestConfig::new().disable_retry())) + .build() + .await; + let oauth = client.oauth(); + + // Spawn the first request that will fail after one second. + let oauth_clone = oauth.clone(); + let first_request = spawn(async move { oauth_clone.server_metadata().await }); + + // Wait to make sure that the first request started. + sleep(Duration::from_millis(200)).await; + + // Spawn the second and third requests. The first that acquires a lock will + // retry the request and succeed, and the second one will read the value from + // the cache. + let oauth_clone = oauth.clone(); + let second_request = spawn(async move { oauth_clone.server_metadata().await }); + let third_request = spawn(async move { oauth.server_metadata().await }); + + first_request.await.expect("task was not cancelled").unwrap_err(); + second_request.await.expect("task was not cancelled").unwrap(); + third_request.await.expect("task was not cancelled").unwrap(); +} + #[async_test] async fn test_server_metadata() { let server = MatrixMockServer::new().await;