async_client: add tests for all current endpoints, event emitter typeing/receipt events, set unread notifications

master
Devin R 2020-05-07 15:21:06 -04:00
parent 14580bc383
commit 4f96ac96e2
5 changed files with 233 additions and 33 deletions

View File

@ -324,16 +324,14 @@ impl AsyncClient {
/// Returns the invited rooms this client knows about. /// Returns the invited rooms this client knows about.
/// ///
/// A `HashMap` of room id to `matrix::models::Room` /// A `HashMap` of room id to `matrix::models::Room`
pub async fn invited_rooms( pub fn invited_rooms(&self) -> Arc<RwLock<HashMap<RoomId, Arc<tokio::sync::RwLock<Room>>>>> {
&self,
) -> Arc<RwLock<HashMap<RoomId, Arc<tokio::sync::RwLock<Room>>>>> {
self.base_client.invited_rooms() self.base_client.invited_rooms()
} }
/// Returns the left rooms this client knows about. /// Returns the left rooms this client knows about.
/// ///
/// A `HashMap` of room id to `matrix::models::Room` /// A `HashMap` of room id to `matrix::models::Room`
pub async fn left_rooms(&self) -> Arc<RwLock<HashMap<RoomId, Arc<tokio::sync::RwLock<Room>>>>> { pub fn left_rooms(&self) -> Arc<RwLock<HashMap<RoomId, Arc<tokio::sync::RwLock<Room>>>>> {
self.base_client.left_rooms() self.base_client.left_rooms()
} }
@ -1196,10 +1194,11 @@ impl AsyncClient {
mod test { mod test {
use super::{ use super::{
ban_user, create_receipt, create_typing_event, forget_room, invite_user, kick_user, ban_user, create_receipt, create_typing_event, forget_room, invite_user, kick_user,
leave_room, leave_room, Invite3pid, MessageEventContent, RoomIdOrAliasId,
}; };
use super::{AsyncClient, AsyncClientConfig, Session, SyncSettings, Url}; use super::{AsyncClient, AsyncClientConfig, Session, SyncSettings, Url};
use crate::events::room::member::MembershipState; use crate::events::room::member::MembershipState;
use crate::events::room::message::TextMessageEventContent;
use crate::identifiers::{EventId, RoomId, UserId}; use crate::identifiers::{EventId, RoomId, UserId};
use matrix_sdk_base::JsonStore; use matrix_sdk_base::JsonStore;
@ -1339,7 +1338,7 @@ mod test {
} }
#[tokio::test] #[tokio::test]
async fn join_room() { async fn join_room_by_id() {
let homeserver = Url::from_str(&mockito::server_url()).unwrap(); let homeserver = Url::from_str(&mockito::server_url()).unwrap();
let session = Session { let session = Session {
@ -1366,9 +1365,41 @@ mod test {
); );
} }
#[tokio::test]
async fn join_room_by_id_or_alias() {
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
let session = Session {
access_token: "1234".to_owned(),
user_id: UserId::try_from("@example:localhost").unwrap(),
device_id: "DEVICEID".to_owned(),
};
let _m = mock(
"POST",
Matcher::Regex(r"^/_matrix/client/r0/join/".to_string()),
)
.with_status(200)
.with_body_from_file("../test_data/room_id.json")
.create();
let client = AsyncClient::new(homeserver, Some(session)).unwrap();
let room_id = RoomIdOrAliasId::try_from("!testroom:example.org").unwrap();
assert_eq!(
// this is the `join_by_room_id::Response` but since no PartialEq we check the RoomId field
client
.join_room_by_id_or_alias(&room_id, &["server.com".to_string()])
.await
.unwrap()
.room_id,
RoomId::try_from("!testroom:example.org").unwrap()
);
}
#[tokio::test] #[tokio::test]
#[allow(irrefutable_let_patterns)] #[allow(irrefutable_let_patterns)]
async fn invite_room() { async fn invite_user_by_id() {
let homeserver = Url::from_str(&mockito::server_url()).unwrap(); let homeserver = Url::from_str(&mockito::server_url()).unwrap();
let user = UserId::try_from("@example:localhost").unwrap(); let user = UserId::try_from("@example:localhost").unwrap();
let room_id = RoomId::try_from("!testroom:example.org").unwrap(); let room_id = RoomId::try_from("!testroom:example.org").unwrap();
@ -1392,6 +1423,44 @@ mod test {
if let invite_user::Response = client.invite_user_by_id(&room_id, &user).await.unwrap() {} if let invite_user::Response = client.invite_user_by_id(&room_id, &user).await.unwrap() {}
} }
#[tokio::test]
#[allow(irrefutable_let_patterns)]
async fn invite_user_by_3pid() {
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
let user = UserId::try_from("@example:localhost").unwrap();
let room_id = RoomId::try_from("!testroom:example.org").unwrap();
let session = Session {
access_token: "1234".to_owned(),
user_id: user.clone(),
device_id: "DEVICEID".to_owned(),
};
let _m = mock(
"POST",
Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/invite".to_string()),
)
.with_status(200)
.with_body_from_file("../test_data/logout_response.json")
.create();
let client = AsyncClient::new(homeserver, Some(session)).unwrap();
if let invite_user::Response = client
.invite_user_by_3pid(
&room_id,
&Invite3pid {
id_server: "example.org".to_string(),
id_access_token: "IdToken".to_string(),
medium: crate::api::r0::thirdparty::Medium::Email,
address: "address".to_string(),
},
)
.await
.unwrap()
{}
}
#[tokio::test] #[tokio::test]
#[allow(irrefutable_let_patterns)] #[allow(irrefutable_let_patterns)]
async fn leave_room() { async fn leave_room() {
@ -1604,6 +1673,48 @@ mod test {
} }
} }
#[tokio::test]
async fn room_message_send() {
use uuid::Uuid;
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
let user = UserId::try_from("@example:localhost").unwrap();
let room_id = RoomId::try_from("!testroom:example.org").unwrap();
let session = Session {
access_token: "1234".to_owned(),
user_id: user.clone(),
device_id: "DEVICEID".to_owned(),
};
let _m = mock(
"PUT",
Matcher::Regex(r"^/_matrix/client/r0/rooms/.*/send/".to_string()),
)
.with_status(200)
.with_body_from_file("../test_data/event_id.json")
.create();
let client = AsyncClient::new(homeserver, Some(session)).unwrap();
let content = MessageEventContent::Text(TextMessageEventContent {
body: "Hello world".to_owned(),
format: None,
formatted_body: None,
relates_to: None,
});
let txn_id = Uuid::new_v4();
let response = client
.room_send(&room_id, content, Some(txn_id))
.await
.unwrap();
assert_eq!(
EventId::try_from("$h29iv0s8:example.com").ok(),
response.event_id
)
}
#[tokio::test] #[tokio::test]
async fn user_presence() { async fn user_presence() {
let homeserver = Url::from_str(&mockito::server_url()).unwrap(); let homeserver = Url::from_str(&mockito::server_url()).unwrap();
@ -1644,35 +1755,102 @@ mod test {
assert!(room.power_levels.is_some()) assert!(room.power_levels.is_some())
} }
// #[tokio::test] #[tokio::test]
// async fn calculate_room_names_from_summary() { async fn calculate_room_names_from_summary() {
// let homeserver = Url::from_str(&mockito::server_url()).unwrap(); let homeserver = Url::from_str(&mockito::server_url()).unwrap();
// let mut bld = EventBuilder::default().build_with_response( let session = Session {
// // this sync has no room.name or room.alias events so only relies on summary access_token: "1234".to_owned(),
// "../test_data/sync_with_summary.json", user_id: UserId::try_from("@example:localhost").unwrap(),
// "GET", device_id: "DEVICEID".to_owned(),
// Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_string()), };
// );
// let session = Session { let _m = mock(
// access_token: "1234".to_owned(), "GET",
// user_id: UserId::try_from("@example:localhost").unwrap(), Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_string()),
// device_id: "DEVICEID".to_owned(), )
// }; .with_status(200)
// let client = AsyncClient::new(homeserver, Some(session)).unwrap(); .with_body_from_file("../test_data/sync_with_summary.json")
// let client = bld.set_client(client).to_client().await.unwrap(); .create();
// let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000)); let client = AsyncClient::new(homeserver, Some(session)).unwrap();
// let _response = client.sync(sync_settings).await.unwrap();
// let mut room_names = vec![]; let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000));
// for room in client.joined_rooms().read().await.values() { let _response = client.sync(sync_settings).await.unwrap();
// room_names.push(room.read().await.display_name())
// }
// assert_eq!(vec!["example, example2"], room_names); let mut room_names = vec![];
// } for room in client.joined_rooms().read().await.values() {
room_names.push(room.read().await.display_name())
}
assert_eq!(vec!["example, example2"], room_names);
}
#[tokio::test]
async fn invited_rooms() {
use std::convert::TryFrom;
let session = crate::Session {
access_token: "12345".to_owned(),
user_id: UserId::try_from("@example:localhost").unwrap(),
device_id: "DEVICEID".to_owned(),
};
let homeserver = url::Url::parse(&mockito::server_url()).unwrap();
let client = AsyncClient::new(homeserver, Some(session)).unwrap();
let _m = mock(
"GET",
Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_string()),
)
.with_status(200)
.with_body_from_file("../test_data/invite_sync.json")
.create();
let _response = client.sync(SyncSettings::default()).await.unwrap();
assert!(client.joined_rooms().read().await.is_empty());
assert!(client.left_rooms().read().await.is_empty());
assert!(!client.invited_rooms().read().await.is_empty());
assert!(client
.get_invited_room(&RoomId::try_from("!696r7674:example.com").unwrap())
.await
.is_some());
}
#[tokio::test]
async fn left_rooms() {
use std::convert::TryFrom;
let session = crate::Session {
access_token: "12345".to_owned(),
user_id: UserId::try_from("@example:localhost").unwrap(),
device_id: "DEVICEID".to_owned(),
};
let homeserver = url::Url::parse(&mockito::server_url()).unwrap();
let client = AsyncClient::new(homeserver, Some(session)).unwrap();
let _m = mock(
"GET",
Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_string()),
)
.with_status(200)
.with_body_from_file("../test_data/leave_sync.json")
.create();
let _response = client.sync(SyncSettings::default()).await.unwrap();
assert!(client.joined_rooms().read().await.is_empty());
assert!(!client.left_rooms().read().await.is_empty());
assert!(client.invited_rooms().read().await.is_empty());
assert!(client
.get_left_room(&RoomId::try_from("!SVkFJHzfwvuaIEawgC:localhost").unwrap())
.await
.is_some())
}
#[tokio::test] #[tokio::test]
async fn test_client_sync_store() { async fn test_client_sync_store() {

View File

@ -662,6 +662,12 @@ impl Client {
.await .await
.set_room_summary(&joined_room.summary); .set_room_summary(&joined_room.summary);
// set unread notification count
matrix_room
.write()
.await
.set_unread_notice_count(&joined_room.unread_notifications);
// re looping is not ideal here // re looping is not ideal here
for event in &mut joined_room.state.events { for event in &mut joined_room.state.events {
if let Ok(e) = event.deserialize() { if let Ok(e) = event.deserialize() {

View File

@ -18,6 +18,7 @@ use crate::events::{
ignored_user_list::IgnoredUserListEvent, ignored_user_list::IgnoredUserListEvent,
presence::PresenceEvent, presence::PresenceEvent,
push_rules::PushRulesEvent, push_rules::PushRulesEvent,
receipt::ReceiptEvent,
room::{ room::{
aliases::AliasesEvent, aliases::AliasesEvent,
avatar::AvatarEvent, avatar::AvatarEvent,
@ -34,6 +35,7 @@ use crate::events::{
StrippedRoomAliases, StrippedRoomAvatar, StrippedRoomCanonicalAlias, StrippedRoomJoinRules, StrippedRoomAliases, StrippedRoomAvatar, StrippedRoomCanonicalAlias, StrippedRoomJoinRules,
StrippedRoomMember, StrippedRoomName, StrippedRoomPowerLevels, StrippedRoomMember, StrippedRoomName, StrippedRoomPowerLevels,
}, },
typing::TypingEvent,
}; };
use crate::RoomState; use crate::RoomState;
@ -143,7 +145,7 @@ pub trait EventEmitter: Send + Sync {
/// Fires when `AsyncClient` receives a `AnyStrippedStateEvent::StrippedRoomJoinRules` event. /// Fires when `AsyncClient` receives a `AnyStrippedStateEvent::StrippedRoomJoinRules` event.
async fn on_stripped_state_join_rules(&self, _: RoomState, _: &StrippedRoomJoinRules) {} async fn on_stripped_state_join_rules(&self, _: RoomState, _: &StrippedRoomJoinRules) {}
// `NonRoomEvent` (this is a type alias from ruma_events) from `IncomingAccountData` // `NonRoomEvent` (this is a type alias from ruma_events)
/// Fires when `AsyncClient` receives a `NonRoomEvent::RoomMember` event. /// Fires when `AsyncClient` receives a `NonRoomEvent::RoomMember` event.
async fn on_account_presence(&self, _: RoomState, _: &PresenceEvent) {} async fn on_account_presence(&self, _: RoomState, _: &PresenceEvent) {}
/// Fires when `AsyncClient` receives a `NonRoomEvent::RoomName` event. /// Fires when `AsyncClient` receives a `NonRoomEvent::RoomName` event.
@ -152,6 +154,12 @@ pub trait EventEmitter: Send + Sync {
async fn on_account_push_rules(&self, _: RoomState, _: &PushRulesEvent) {} async fn on_account_push_rules(&self, _: RoomState, _: &PushRulesEvent) {}
/// Fires when `AsyncClient` receives a `NonRoomEvent::RoomAliases` event. /// Fires when `AsyncClient` receives a `NonRoomEvent::RoomAliases` event.
async fn on_account_data_fully_read(&self, _: RoomState, _: &FullyReadEvent) {} async fn on_account_data_fully_read(&self, _: RoomState, _: &FullyReadEvent) {}
/// Fires when `AsyncClient` receives a `NonRoomEvent::Typing` event.
async fn on_account_data_typing(&self, _: RoomState, _: &TypingEvent) {}
/// Fires when `AsyncClient` receives a `NonRoomEvent::Receipt` event.
///
/// This is always a read receipt.
async fn on_account_data_receipt(&self, _: RoomState, _: &ReceiptEvent) {}
// `PresenceEvent` is a struct so there is only the one method // `PresenceEvent` is a struct so there is only the one method
/// Fires when `AsyncClient` receives a `NonRoomEvent::RoomAliases` event. /// Fires when `AsyncClient` receives a `NonRoomEvent::RoomAliases` event.

View File

@ -20,7 +20,7 @@ use std::convert::TryFrom;
use super::message::MessageQueue; use super::message::MessageQueue;
use super::RoomMember; use super::RoomMember;
use crate::api::r0::sync::sync_events::RoomSummary; use crate::api::r0::sync::sync_events::{RoomSummary, UnreadNotificationsCount};
use crate::events::collections::all::{RoomEvent, StateEvent}; use crate::events::collections::all::{RoomEvent, StateEvent};
use crate::events::presence::PresenceEvent; use crate::events::presence::PresenceEvent;
use crate::events::room::{ use crate::events::room::{
@ -320,6 +320,11 @@ impl Room {
self.room_name.joined_member_count = *joined_member_count; self.room_name.joined_member_count = *joined_member_count;
} }
pub(crate) fn set_unread_notice_count(&mut self, notifications: &UnreadNotificationsCount) {
self.unread_highlight = notifications.highlight_count;
self.unread_notifications = notifications.notification_count;
}
/// Handle a room.member updating the room state if necessary. /// Handle a room.member updating the room state if necessary.
/// ///
/// Returns true if the joined member list changed, false otherwise. /// Returns true if the joined member list changed, false otherwise.

3
test_data/event_id.json Normal file
View File

@ -0,0 +1,3 @@
{
"event_id": "$h29iv0s8:example.com"
}