use matrix_sdk_base::{SessionMeta, store::RoomLoadSettings};
use ruma::{OwnedDeviceId, OwnedUserId, api::MatrixVersion, owned_device_id, owned_user_id};
use crate::{
Client, ClientBuilder, SessionTokens, authentication::matrix::MatrixSession,
config::RequestConfig,
};
#[allow(missing_debug_implementations)]
pub struct MockClientBuilder {
builder: ClientBuilder,
auth_state: AuthState,
server_versions: ServerVersions,
}
impl MockClientBuilder {
pub fn new(homeserver: Option<&str>) -> Self {
let homeserver = homeserver.unwrap_or("http://localhost");
let default_builder = Client::builder()
.homeserver_url(homeserver)
.request_config(RequestConfig::new().disable_retry());
Self {
builder: default_builder,
auth_state: AuthState::LoggedInWithMatrixAuth {
token: None,
user_id: None,
device_id: None,
},
server_versions: ServerVersions::Default,
}
}
pub fn no_server_versions(mut self) -> Self {
self.server_versions = ServerVersions::None;
self
}
pub fn server_versions(mut self, versions: Vec<MatrixVersion>) -> Self {
self.server_versions = ServerVersions::Custom(versions);
self
}
pub fn unlogged(mut self) -> Self {
self.auth_state = AuthState::None;
self
}
pub fn registered_with_oauth(mut self) -> Self {
self.auth_state = AuthState::RegisteredWithOAuth;
self
}
pub fn logged_in_with_oauth(mut self) -> Self {
self.auth_state = AuthState::LoggedInWithOAuth;
self
}
pub fn logged_in_with_token(
mut self,
token: String,
user_id: OwnedUserId,
device_id: OwnedDeviceId,
) -> Self {
self.auth_state = AuthState::LoggedInWithMatrixAuth {
token: Some(token),
user_id: Some(user_id),
device_id: Some(device_id),
};
self
}
pub fn on_builder<F: FnOnce(ClientBuilder) -> ClientBuilder>(mut self, f: F) -> Self {
self.builder = f(self.builder);
self
}
pub async fn build(self) -> Client {
let mut builder = self.builder;
if let Some(versions) = self.server_versions.into_vec() {
builder = builder.server_versions(versions);
}
let client = builder.build().await.expect("building client failed");
self.auth_state.maybe_restore_client(&client).await;
client
}
}
enum AuthState {
None,
LoggedInWithMatrixAuth {
token: Option<String>,
user_id: Option<OwnedUserId>,
device_id: Option<OwnedDeviceId>,
},
RegisteredWithOAuth,
LoggedInWithOAuth,
}
impl AuthState {
async fn maybe_restore_client(self, client: &Client) {
match self {
AuthState::None => {}
AuthState::LoggedInWithMatrixAuth { token, user_id, device_id } => {
client
.matrix_auth()
.restore_session(
MatrixSession {
meta: SessionMeta {
user_id: user_id.unwrap_or(owned_user_id!("@example:localhost")),
device_id: device_id.unwrap_or(owned_device_id!("DEVICEID")),
},
tokens: SessionTokens {
access_token: token.unwrap_or("1234".to_owned()).to_owned(),
refresh_token: None,
},
},
RoomLoadSettings::default(),
)
.await
.unwrap();
}
AuthState::RegisteredWithOAuth => {
client.oauth().restore_registered_client(oauth::mock_client_id());
}
AuthState::LoggedInWithOAuth => {
client
.oauth()
.restore_session(
oauth::mock_session(mock_session_tokens_with_refresh()),
RoomLoadSettings::default(),
)
.await
.unwrap();
}
}
}
}
enum ServerVersions {
Default,
None,
Custom(Vec<MatrixVersion>),
}
impl ServerVersions {
fn into_vec(self) -> Option<Vec<MatrixVersion>> {
match self {
Self::Default => Some(vec![MatrixVersion::V1_12]),
Self::None => None,
Self::Custom(versions) => Some(versions),
}
}
}
pub fn mock_session_meta() -> SessionMeta {
SessionMeta {
user_id: owned_user_id!("@example:localhost"),
device_id: owned_device_id!("DEVICEID"),
}
}
pub fn mock_session_tokens() -> SessionTokens {
SessionTokens { access_token: "1234".to_owned(), refresh_token: None }
}
pub fn mock_session_tokens_with_refresh() -> SessionTokens {
SessionTokens { access_token: "1234".to_owned(), refresh_token: Some("ZYXWV".to_owned()) }
}
pub fn mock_prev_session_tokens_with_refresh() -> SessionTokens {
SessionTokens {
access_token: "prev-access-token".to_owned(),
refresh_token: Some("prev-refresh-token".to_owned()),
}
}
pub fn mock_matrix_session() -> MatrixSession {
MatrixSession { meta: mock_session_meta(), tokens: mock_session_tokens() }
}
pub mod oauth {
use ruma::serde::Raw;
use url::Url;
use crate::{
SessionTokens,
authentication::oauth::{
ClientId, OAuthSession, UserSession,
registration::{ApplicationType, ClientMetadata, Localized, OAuthGrantType},
},
};
pub fn mock_client_id() -> ClientId {
ClientId::new("test_client_id".to_owned())
}
pub fn mock_redirect_uri() -> Url {
Url::parse("http://127.0.0.1/").expect("redirect URI should be valid")
}
pub fn mock_client_metadata() -> Raw<ClientMetadata> {
let client_uri = Url::parse("https://github.com/matrix-org/matrix-rust-sdk")
.expect("client URI should be valid");
let mut metadata = ClientMetadata::new(
ApplicationType::Native,
vec![
OAuthGrantType::AuthorizationCode { redirect_uris: vec![mock_redirect_uri()] },
OAuthGrantType::DeviceCode,
],
Localized::new(client_uri, None),
);
metadata.client_name = Some(Localized::new("matrix-rust-sdk-test".to_owned(), None));
Raw::new(&metadata).expect("client metadata should serialize successfully")
}
pub fn mock_session(tokens: SessionTokens) -> OAuthSession {
OAuthSession {
client_id: mock_client_id(),
user: UserSession { meta: super::mock_session_meta(), tokens },
}
}
}