feat: Move Client::get_dm_room into the main impl Client block

This patch moves the `Client::get_dm_room` helper function and its tests
from `src/encryption/mod.rs` to `src/client/mod.rs`, so it may be used
without the `e2e-encryption` crate feature enabled.

- [x] Public API changes documented in changelogs (optional)

Signed-off-by: Ginger <ginger@gingershaped.computer>
This commit is contained in:
Ginger
2025-10-16 10:03:09 -04:00
committed by GitHub
parent e8fb133cbf
commit a4e68ba885
3 changed files with 101 additions and 102 deletions
+2
View File
@@ -8,6 +8,8 @@ All notable changes to this project will be documented in this file.
### Features
- Allow `Client::get_dm_room()` to be called without the `e2e-encryption` crate feature.
([#5787](https://github.com/matrix-org/matrix-rust-sdk/pull/5787))
- [**breaking**] Add `encryption::secret_storage::SecretStorageError::ImportError` to indicate
an error that occurred when importing a secret from secret storage.
([#5647](https://github.com/matrix-org/matrix-rust-sdk/pull/5647))
+95 -1
View File
@@ -73,6 +73,7 @@ use ruma::{
federation::discovery::get_server_version,
},
assign,
events::direct::DirectUserIdentifier,
push::Ruleset,
time::Instant,
};
@@ -1800,6 +1801,20 @@ impl Client {
self.create_room(request).await
}
/// Get the existing DM room with the given user, if any.
pub fn get_dm_room(&self, user_id: &UserId) -> Option<Room> {
let rooms = self.joined_rooms();
// Find the room we share with the `user_id` and only with `user_id`
let room = rooms.into_iter().find(|r| {
let targets = r.direct_targets();
targets.len() == 1 && targets.contains(<&DirectUserIdentifier>::from(user_id))
});
trace!(?user_id, ?room, "Found DM room with user");
room
}
/// Search the homeserver's directory for public rooms with a filter.
///
/// # Arguments
@@ -3122,7 +3137,7 @@ pub(crate) mod tests {
ignored_user_list::IgnoredUserListEventContent,
media_preview_config::{InviteAvatars, MediaPreviewConfigEventContent, MediaPreviews},
},
owned_room_id, owned_user_id, room_alias_id, room_id,
owned_room_id, owned_user_id, room_alias_id, room_id, user_id,
};
use serde_json::json;
use stream_assert::{assert_next_matches, assert_pending};
@@ -4112,4 +4127,83 @@ pub(crate) mod tests {
.await
.unwrap();
}
#[async_test]
async fn test_get_dm_room_returns_the_room_we_have_with_this_user() {
let server = MatrixMockServer::new().await;
let client = server.client_builder().build().await;
// This is the user ID that is inside MemberAdditional.
// Note the confusing username, so we can share
// GlobalAccountDataTestEvent::Direct with the invited test.
let user_id = user_id!("@invited:localhost");
// When we receive a sync response saying "invited" is invited to a DM
let f = EventFactory::new();
let response = SyncResponseBuilder::default()
.add_joined_room(
JoinedRoomBuilder::default().add_state_event(StateTestEvent::MemberAdditional),
)
.add_global_account_data(
f.direct().add_user(user_id.to_owned().into(), *DEFAULT_TEST_ROOM_ID),
)
.build_sync_response();
client.base_client().receive_sync_response(response).await.unwrap();
// Then get_dm_room finds this room
let found_room = client.get_dm_room(user_id).expect("DM not found!");
assert!(found_room.get_member_no_sync(user_id).await.unwrap().is_some());
}
#[async_test]
async fn test_get_dm_room_still_finds_room_where_participant_is_only_invited() {
let server = MatrixMockServer::new().await;
let client = server.client_builder().build().await;
// This is the user ID that is inside MemberInvite
let user_id = user_id!("@invited:localhost");
// When we receive a sync response saying "invited" is invited to a DM
let f = EventFactory::new();
let response = SyncResponseBuilder::default()
.add_joined_room(
JoinedRoomBuilder::default().add_state_event(StateTestEvent::MemberInvite),
)
.add_global_account_data(
f.direct().add_user(user_id.to_owned().into(), *DEFAULT_TEST_ROOM_ID),
)
.build_sync_response();
client.base_client().receive_sync_response(response).await.unwrap();
// Then get_dm_room finds this room
let found_room = client.get_dm_room(user_id).expect("DM not found!");
assert!(found_room.get_member_no_sync(user_id).await.unwrap().is_some());
}
#[async_test]
async fn test_get_dm_room_still_finds_left_room() {
// See the discussion in https://github.com/matrix-org/matrix-rust-sdk/issues/2017
// and the high-level issue at https://github.com/vector-im/element-x-ios/issues/1077
let server = MatrixMockServer::new().await;
let client = server.client_builder().build().await;
// This is the user ID that is inside MemberAdditional.
// Note the confusing username, so we can share
// GlobalAccountDataTestEvent::Direct with the invited test.
let user_id = user_id!("@invited:localhost");
// When we receive a sync response saying "invited" is invited to a DM
let f = EventFactory::new();
let response = SyncResponseBuilder::default()
.add_joined_room(
JoinedRoomBuilder::default().add_state_event(StateTestEvent::MemberLeave),
)
.add_global_account_data(
f.direct().add_user(user_id.to_owned().into(), *DEFAULT_TEST_ROOM_ID),
)
.build_sync_response();
client.base_client().receive_sync_response(response).await.unwrap();
// Then get_dm_room finds this room
let found_room = client.get_dm_room(user_id).expect("DM not found!");
assert!(found_room.get_member_no_sync(user_id).await.unwrap().is_some());
}
}
+4 -101
View File
@@ -64,10 +64,7 @@ use ruma::{
uiaa::{AuthData, UiaaInfo},
},
assign,
events::{
direct::DirectUserIdentifier,
room::{MediaSource, ThumbnailInfo},
},
events::room::{MediaSource, ThumbnailInfo},
};
#[cfg(feature = "experimental-send-custom-to-device")]
use ruma::{events::AnyToDeviceEventContent, serde::Raw, to_device::DeviceIdOrAllDevices};
@@ -75,7 +72,7 @@ use serde::{Deserialize, de::Error as _};
use tasks::BundleReceiverTask;
use tokio::sync::{Mutex, RwLockReadGuard};
use tokio_stream::wrappers::errors::BroadcastStreamRecvError;
use tracing::{debug, error, instrument, trace, warn};
use tracing::{debug, error, instrument, warn};
use url::Url;
use vodozemac::Curve25519PublicKey;
@@ -89,7 +86,7 @@ use self::{
verification::{SasVerification, Verification, VerificationRequest},
};
use crate::{
Client, Error, HttpError, Result, Room, RumaApiError, TransmissionProgress,
Client, Error, HttpError, Result, RumaApiError, TransmissionProgress,
attachment::Thumbnail,
client::{ClientInner, WeakClient},
cross_process_lock::CrossProcessLockGuard,
@@ -669,20 +666,6 @@ impl Client {
Ok(())
}
/// Get the existing DM room with the given user, if any.
pub fn get_dm_room(&self, user_id: &UserId) -> Option<Room> {
let rooms = self.joined_rooms();
// Find the room we share with the `user_id` and only with `user_id`
let room = rooms.into_iter().find(|r| {
let targets = r.direct_targets();
targets.len() == 1 && targets.contains(<&DirectUserIdentifier>::from(user_id))
});
trace!(?room, "Found room");
room
}
async fn send_outgoing_request(&self, r: OutgoingRequest) -> Result<()> {
use matrix_sdk_base::crypto::types::requests::AnyOutgoingRequest;
@@ -2034,12 +2017,11 @@ mod tests {
use matrix_sdk_test::{
DEFAULT_TEST_ROOM_ID, JoinedRoomBuilder, StateTestEvent, SyncResponseBuilder, async_test,
event_factory::EventFactory, test_json,
test_json,
};
use ruma::{
event_id,
events::{reaction::ReactionEventContent, relation::Annotation},
user_id,
};
use serde_json::json;
use wiremock::{
@@ -2106,85 +2088,6 @@ mod tests {
room.send_raw("m.reaction", json!({})).await.expect("Sending the reaction should not fail");
}
#[async_test]
async fn test_get_dm_room_returns_the_room_we_have_with_this_user() {
let server = MockServer::start().await;
let client = logged_in_client(Some(server.uri())).await;
// This is the user ID that is inside MemberAdditional.
// Note the confusing username, so we can share
// GlobalAccountDataTestEvent::Direct with the invited test.
let user_id = user_id!("@invited:localhost");
// When we receive a sync response saying "invited" is invited to a DM
let f = EventFactory::new();
let response = SyncResponseBuilder::default()
.add_joined_room(
JoinedRoomBuilder::default().add_state_event(StateTestEvent::MemberAdditional),
)
.add_global_account_data(
f.direct().add_user(user_id.to_owned().into(), *DEFAULT_TEST_ROOM_ID),
)
.build_sync_response();
client.base_client().receive_sync_response(response).await.unwrap();
// Then get_dm_room finds this room
let found_room = client.get_dm_room(user_id).expect("DM not found!");
assert!(found_room.get_member_no_sync(user_id).await.unwrap().is_some());
}
#[async_test]
async fn test_get_dm_room_still_finds_room_where_participant_is_only_invited() {
let server = MockServer::start().await;
let client = logged_in_client(Some(server.uri())).await;
// This is the user ID that is inside MemberInvite
let user_id = user_id!("@invited:localhost");
// When we receive a sync response saying "invited" is invited to a DM
let f = EventFactory::new();
let response = SyncResponseBuilder::default()
.add_joined_room(
JoinedRoomBuilder::default().add_state_event(StateTestEvent::MemberInvite),
)
.add_global_account_data(
f.direct().add_user(user_id.to_owned().into(), *DEFAULT_TEST_ROOM_ID),
)
.build_sync_response();
client.base_client().receive_sync_response(response).await.unwrap();
// Then get_dm_room finds this room
let found_room = client.get_dm_room(user_id).expect("DM not found!");
assert!(found_room.get_member_no_sync(user_id).await.unwrap().is_some());
}
#[async_test]
async fn test_get_dm_room_still_finds_left_room() {
// See the discussion in https://github.com/matrix-org/matrix-rust-sdk/issues/2017
// and the high-level issue at https://github.com/vector-im/element-x-ios/issues/1077
let server = MockServer::start().await;
let client = logged_in_client(Some(server.uri())).await;
// This is the user ID that is inside MemberAdditional.
// Note the confusing username, so we can share
// GlobalAccountDataTestEvent::Direct with the invited test.
let user_id = user_id!("@invited:localhost");
// When we receive a sync response saying "invited" is invited to a DM
let f = EventFactory::new();
let response = SyncResponseBuilder::default()
.add_joined_room(
JoinedRoomBuilder::default().add_state_event(StateTestEvent::MemberLeave),
)
.add_global_account_data(
f.direct().add_user(user_id.to_owned().into(), *DEFAULT_TEST_ROOM_ID),
)
.build_sync_response();
client.base_client().receive_sync_response(response).await.unwrap();
// Then get_dm_room finds this room
let found_room = client.get_dm_room(user_id).expect("DM not found!");
assert!(found_room.get_member_no_sync(user_id).await.unwrap().is_some());
}
#[cfg(feature = "sqlite")]
#[async_test]
async fn test_generation_counter_invalidates_olm_machine() {