1#[cfg(feature = "e2e-encryption")]
18use matrix_sdk_common::deserialized_responses::ProcessedToDeviceEvent;
19use matrix_sdk_common::timer;
20use ruma::{
21 OwnedRoomId, api::client::sync::sync_events::v5 as http, events::receipt::SyncReceiptEvent,
22 serde::Raw,
23};
24use tokio::sync::MutexGuard;
25use tracing::{instrument, trace};
26
27use super::BaseClient;
28use crate::{
29 RequestedRequiredStates,
30 error::Result,
31 response_processors as processors,
32 store::{AvatarCache, ambiguity_map::AmbiguityCache},
33 sync::{RoomUpdates, SyncResponse},
34};
35
36impl BaseClient {
37 #[cfg(feature = "e2e-encryption")]
45 pub async fn process_sliding_sync_e2ee(
46 &self,
47 to_device: Option<&http::response::ToDevice>,
48 e2ee: &http::response::E2EE,
49 state_store_guard: &MutexGuard<'_, ()>,
50 ) -> Result<Option<Vec<ProcessedToDeviceEvent>>> {
51 if to_device.is_none() && e2ee.is_empty() {
52 return Ok(None);
53 }
54
55 trace!(
56 to_device_events =
57 to_device.map(|to_device| to_device.events.len()).unwrap_or_default(),
58 device_one_time_keys_count = e2ee.device_one_time_keys_count.len(),
59 device_unused_fallback_key_types =
60 e2ee.device_unused_fallback_key_types.as_ref().map(|v| v.len()),
61 "Processing sliding sync e2ee events",
62 );
63
64 let olm_machine = self.olm_machine().await;
65
66 let context = processors::Context::default();
67
68 let processors::e2ee::to_device::Output { processed_to_device_events } =
69 processors::e2ee::to_device::from_msc4186(
70 to_device,
71 e2ee,
72 olm_machine.as_ref(),
73 &self.decryption_settings,
74 )
75 .await?;
76
77 processors::changes::save_and_apply(
78 context,
79 &self.state_store,
80 state_store_guard,
81 &self.ignore_user_list_changes,
82 None,
83 )
84 .await?;
85
86 Ok(Some(processed_to_device_events))
87 }
88
89 #[instrument(skip_all, level = "trace")]
96 pub async fn process_sliding_sync(
97 &self,
98 response: &http::Response,
99 requested_required_states: &RequestedRequiredStates,
100 state_store_guard: &MutexGuard<'_, ()>,
101 ) -> Result<SyncResponse> {
102 let http::Response { rooms, lists, extensions, .. } = response;
103
104 trace!(
105 rooms = rooms.len(),
106 lists = lists.len(),
107 has_extensions = !extensions.is_empty(),
108 "Processing sliding sync room events"
109 );
110
111 if rooms.is_empty() && extensions.is_empty() {
112 return Ok(SyncResponse::default());
115 }
116
117 let _timer = timer!(tracing::Level::TRACE, "_method");
118
119 let mut context = processors::Context::default();
120
121 let state_store = self.state_store.clone();
122 let mut ambiguity_cache = AmbiguityCache::new(state_store.inner.clone());
123 let mut avatar_cache = AvatarCache::new(state_store.inner.clone());
124
125 let global_account_data_processor =
126 processors::account_data::global(&extensions.account_data.global);
127 let push_rules = self.get_push_rules(&global_account_data_processor).await?;
128
129 let mut room_updates = RoomUpdates::default();
130 let mut notifications = Default::default();
131
132 let user_id = self
133 .session_meta()
134 .expect("Sliding sync shouldn't run without an authenticated user")
135 .user_id
136 .to_owned();
137
138 for (room_id, room_response) in rooms {
139 let Some((room_info, room_update)) = processors::room::msc4186::update_any_room(
140 &mut context,
141 &user_id,
142 processors::room::RoomCreationData::new(
143 room_id,
144 requested_required_states,
145 &mut ambiguity_cache,
146 &mut avatar_cache,
147 ),
148 room_response,
149 &extensions.account_data.rooms,
150 #[cfg(feature = "e2e-encryption")]
151 processors::e2ee::E2EE::new(
152 self.olm_machine().await.as_ref(),
153 &self.decryption_settings,
154 self.handle_verification_events,
155 ),
156 processors::notification::Notification::new(
157 &push_rules,
158 &mut notifications,
159 &self.state_store,
160 ),
161 )
162 .await?
163 else {
164 continue;
165 };
166
167 context.state_changes.add_room(room_info);
168
169 let room_id = room_id.to_owned();
170
171 use processors::room::msc4186::RoomUpdateKind;
172
173 match room_update {
174 RoomUpdateKind::Joined(joined_room_update) => {
175 room_updates.joined.insert(room_id, joined_room_update);
176 }
177 RoomUpdateKind::Left(left_room_update) => {
178 room_updates.left.insert(room_id, left_room_update);
179 }
180 RoomUpdateKind::Invited(invited_room_update) => {
181 room_updates.invited.insert(room_id, invited_room_update);
182 }
183 RoomUpdateKind::Knocked(knocked_room_update) => {
184 room_updates.knocked.insert(room_id, knocked_room_update);
185 }
186 }
187 }
188
189 processors::room::msc4186::extensions::dispatch_typing_ephemeral_events(
193 &extensions.typing,
194 &mut room_updates.joined,
195 );
196
197 processors::room::msc4186::extensions::room_account_data(
199 &mut context,
200 &extensions.account_data,
201 &mut room_updates,
202 &self.state_store,
203 );
204
205 global_account_data_processor.apply(&mut context, &state_store).await;
206
207 context.state_changes.ambiguity_maps = ambiguity_cache.cache;
208
209 processors::changes::save_and_apply(
211 context,
212 &self.state_store,
213 state_store_guard,
214 &self.ignore_user_list_changes,
215 None,
216 )
217 .await?;
218
219 let mut context = processors::Context::default();
220
221 processors::room::display_name::update_for_rooms(
224 &mut context,
225 &room_updates,
226 &self.state_store,
227 )
228 .await;
229
230 processors::changes::save_only(context, &self.state_store, state_store_guard).await?;
232
233 Ok(SyncResponse {
234 rooms: room_updates,
235 notifications,
236 presence: Default::default(),
237 account_data: extensions.account_data.global.clone(),
238 to_device: Default::default(),
239 })
240 }
241
242 #[doc(hidden)]
245 pub async fn process_sliding_sync_receipts_extension_for_room(
246 &self,
247 room_id: &OwnedRoomId,
248 response: &http::Response,
249 state_store_guard: &MutexGuard<'_, ()>,
250 ) -> Result<Option<Raw<SyncReceiptEvent>>> {
251 let mut context = processors::Context::default();
252
253 let mut save_context = false;
254
255 let receipt_ephemeral_event = if let Some(receipt_ephemeral_event) =
257 response.extensions.receipts.rooms.get(room_id)
258 {
259 processors::room::msc4186::extensions::dispatch_receipt_ephemeral_event_for_room(
260 &mut context,
261 room_id,
262 receipt_ephemeral_event,
263 );
264 save_context = true;
265 Some(receipt_ephemeral_event.clone())
266 } else {
267 None
268 };
269
270 if save_context {
272 processors::changes::save_only(context, &self.state_store, state_store_guard).await?;
273 }
274
275 Ok(receipt_ephemeral_event)
276 }
277}
278
279#[cfg(all(test, not(target_family = "wasm")))]
280mod tests {
281 use std::collections::{BTreeMap, HashSet};
282
283 use assert_matches::assert_matches;
284 use matrix_sdk_test::async_test;
285 use ruma::{
286 JsOption, MxcUri, OwnedRoomId, OwnedUserId, RoomAliasId, RoomId, UserId,
287 api::client::sync::sync_events::UnreadNotificationsCount,
288 assign, event_id,
289 events::{
290 GlobalAccountDataEventContent, StateEventContent, StateEventType,
291 direct::{DirectEventContent, DirectUserIdentifier, OwnedDirectUserIdentifier},
292 room::{
293 avatar::RoomAvatarEventContent,
294 canonical_alias::RoomCanonicalAliasEventContent,
295 encryption::RoomEncryptionEventContent,
296 member::{MembershipState, RoomMemberEventContent},
297 name::RoomNameEventContent,
298 pinned_events::RoomPinnedEventsEventContent,
299 },
300 },
301 mxc_uri, owned_event_id, owned_mxc_uri, owned_user_id, room_alias_id, room_id,
302 serde::Raw,
303 uint, user_id,
304 };
305 use serde_json::json;
306
307 use super::http;
308 use crate::{
309 BaseClient, DmRoomDefinition, EncryptionState, RequestedRequiredStates,
310 RoomInfoNotableUpdate, RoomState, SessionMeta,
311 client::ThreadingSupport,
312 room::{RoomHero, RoomInfoNotableUpdateReasons},
313 store::{RoomLoadSettings, StoreConfig},
314 test_utils::logged_in_base_client,
315 };
316
317 #[async_test]
318 async fn test_invited_state_without_update_emits_invited_room() {
319 let client = logged_in_base_client(None).await;
320 let room_id = room_id!("!invite:e.uk");
321 let user_id = client.session_meta().unwrap().user_id.to_owned();
322
323 let mut room = http::response::Room::new();
324 room.invite_state = Some(invite_state_for(&user_id, MembershipState::Invite));
325
326 let response = response_with_room(room_id, room);
327
328 let sync_resp = client
329 .process_sliding_sync(
330 &response,
331 &RequestedRequiredStates::default(),
332 &client.state_store_lock().lock().await,
333 )
334 .await
335 .unwrap();
336
337 assert!(sync_resp.rooms.invited.contains_key(room_id));
338 }
339
340 use matrix_sdk_common::cross_process_lock::CrossProcessLockConfig;
341 use ruma::events::AnyStrippedStateEvent;
342
343 fn invite_state_for(
344 user_id: &UserId,
345 membership: MembershipState,
346 ) -> Vec<Raw<AnyStrippedStateEvent>> {
347 let content = RoomMemberEventContent::new(membership);
348
349 let raw: Raw<AnyStrippedStateEvent> = Raw::from_json_string(
350 serde_json::json!({
351 "type": "m.room.member",
352 "state_key": user_id,
353 "content": content,
354 })
355 .to_string(),
356 )
357 .unwrap();
358
359 vec![raw]
360 }
361
362 #[async_test]
363 async fn test_knocked_state_emits_invited_room() {
364 let client = logged_in_base_client(None).await;
365 let room_id = room_id!("!knock:e.uk");
366 let user_id = client.session_meta().unwrap().user_id.to_owned();
367
368 let mut room = http::response::Room::new();
369 room.invite_state = Some(invite_state_for(&user_id, MembershipState::Knock));
370
371 let response = response_with_room(room_id, room);
372
373 let sync_resp = client
374 .process_sliding_sync(
375 &response,
376 &RequestedRequiredStates::default(),
377 &client.state_store_lock().lock().await,
378 )
379 .await
380 .unwrap();
381
382 assert!(sync_resp.rooms.invited.contains_key(room_id));
384 }
385
386 #[async_test]
387 async fn test_notification_count_set() {
388 let client = logged_in_base_client(None).await;
389
390 let mut response = http::Response::new("42".to_owned());
391 let room_id = room_id!("!room:example.org");
392 let count = assign!(UnreadNotificationsCount::default(), {
393 highlight_count: Some(uint!(13)),
394 notification_count: Some(uint!(37)),
395 });
396
397 response.rooms.insert(
398 room_id.to_owned(),
399 assign!(http::response::Room::new(), {
400 unread_notifications: count.clone()
401 }),
402 );
403
404 let sync_response = client
405 .process_sliding_sync(
406 &response,
407 &RequestedRequiredStates::default(),
408 &client.state_store_lock().lock().await,
409 )
410 .await
411 .expect("Failed to process sync");
412
413 let room = sync_response.rooms.joined.get(room_id).unwrap();
415 assert_eq!(room.unread_notifications, count.clone().into());
416
417 let room = client.get_room(room_id).expect("found room");
419 assert_eq!(room.unread_notification_counts(), count.into());
420 }
421
422 #[async_test]
423 async fn test_can_process_empty_sliding_sync_response() {
424 let client = logged_in_base_client(None).await;
425 let empty_response = http::Response::new("5".to_owned());
426 client
427 .process_sliding_sync(
428 &empty_response,
429 &RequestedRequiredStates::default(),
430 &client.state_store_lock().lock().await,
431 )
432 .await
433 .expect("Failed to process sync");
434 }
435
436 #[async_test]
437 async fn test_room_with_unspecified_state_is_added_to_client_and_joined_list() {
438 let client = logged_in_base_client(None).await;
440 let room_id = room_id!("!r:e.uk");
441
442 let mut room = http::response::Room::new();
445 room.joined_count = Some(uint!(41));
446 let response = response_with_room(room_id, room);
447 let sync_resp = client
448 .process_sliding_sync(
449 &response,
450 &RequestedRequiredStates::default(),
451 &client.state_store_lock().lock().await,
452 )
453 .await
454 .expect("Failed to process sync");
455
456 let client_room = client.get_room(room_id).expect("No room found");
458 assert_eq!(client_room.room_id(), room_id);
459 assert_eq!(client_room.joined_members_count(), 41);
460 assert_eq!(client_room.state(), RoomState::Joined);
461
462 assert!(sync_resp.rooms.joined.contains_key(room_id));
464 assert!(!sync_resp.rooms.left.contains_key(room_id));
465 assert!(!sync_resp.rooms.invited.contains_key(room_id));
466 }
467
468 #[async_test]
469 async fn test_missing_room_name_event() {
470 let client = logged_in_base_client(None).await;
472 let room_id = room_id!("!r:e.uk");
473
474 let mut room = http::response::Room::new();
477 room.name = Some("little room".to_owned());
478 let response = response_with_room(room_id, room);
479 let sync_resp = client
480 .process_sliding_sync(
481 &response,
482 &RequestedRequiredStates::default(),
483 &client.state_store_lock().lock().await,
484 )
485 .await
486 .expect("Failed to process sync");
487
488 let client_room = client.get_room(room_id).expect("No room found");
490 assert!(client_room.name().is_none());
491 assert_eq!(
492 client_room.compute_display_name().await.unwrap().into_inner().to_string(),
493 "Empty Room"
494 );
495 assert_eq!(client_room.state(), RoomState::Joined);
496
497 assert!(sync_resp.rooms.joined.contains_key(room_id));
499 assert!(!sync_resp.rooms.left.contains_key(room_id));
500 assert!(!sync_resp.rooms.invited.contains_key(room_id));
501 assert!(!sync_resp.rooms.knocked.contains_key(room_id));
502 }
503
504 #[async_test]
505 async fn test_room_name_event() {
506 let client = logged_in_base_client(None).await;
508 let room_id = room_id!("!r:e.uk");
509
510 let mut room = http::response::Room::new();
513
514 room.name = Some("little room".to_owned());
515 set_room_name(&mut room, user_id!("@a:b.c"), "The Name".to_owned());
516
517 let response = response_with_room(room_id, room);
518 client
519 .process_sliding_sync(
520 &response,
521 &RequestedRequiredStates::default(),
522 &client.state_store_lock().lock().await,
523 )
524 .await
525 .expect("Failed to process sync");
526
527 let client_room = client.get_room(room_id).expect("No room found");
529 assert_eq!(client_room.name().as_deref(), Some("The Name"));
530 assert_eq!(
531 client_room.compute_display_name().await.unwrap().into_inner().to_string(),
532 "The Name"
533 );
534 }
535
536 #[async_test]
537 async fn test_missing_invited_room_name_event() {
538 let client = logged_in_base_client(None).await;
540 let room_id = room_id!("!r:e.uk");
541 let user_id = user_id!("@w:e.uk");
542 let inviter = user_id!("@john:mastodon.org");
543
544 let mut room = http::response::Room::new();
547 set_room_invited(&mut room, inviter, user_id);
548 room.name = Some("name from sliding sync response".to_owned());
549 let response = response_with_room(room_id, room);
550 let sync_resp = client
551 .process_sliding_sync(
552 &response,
553 &RequestedRequiredStates::default(),
554 &client.state_store_lock().lock().await,
555 )
556 .await
557 .expect("Failed to process sync");
558
559 let client_room = client.get_room(room_id).expect("No room found");
561 assert!(client_room.name().is_none());
562
563 assert_eq!(client_room.compute_display_name().await.unwrap().into_inner().to_string(), "w");
565
566 assert_eq!(client_room.state(), RoomState::Invited);
567
568 assert!(!sync_resp.rooms.joined.contains_key(room_id));
570 assert!(!sync_resp.rooms.left.contains_key(room_id));
571 assert!(sync_resp.rooms.invited.contains_key(room_id));
572 assert!(!sync_resp.rooms.knocked.contains_key(room_id));
573 }
574
575 #[async_test]
576 async fn test_invited_room_name_event() {
577 let client = logged_in_base_client(None).await;
579 let room_id = room_id!("!r:e.uk");
580 let user_id = user_id!("@w:e.uk");
581 let inviter = user_id!("@john:mastodon.org");
582
583 let mut room = http::response::Room::new();
586
587 set_room_invited(&mut room, inviter, user_id);
588
589 room.name = Some("name from sliding sync response".to_owned());
590 set_room_name(&mut room, user_id!("@a:b.c"), "The Name".to_owned());
591
592 let response = response_with_room(room_id, room);
593 client
594 .process_sliding_sync(
595 &response,
596 &RequestedRequiredStates::default(),
597 &client.state_store_lock().lock().await,
598 )
599 .await
600 .expect("Failed to process sync");
601
602 let client_room = client.get_room(room_id).expect("No room found");
604 assert_eq!(client_room.name().as_deref(), Some("The Name"));
605 assert_eq!(
606 client_room.compute_display_name().await.unwrap().into_inner().to_string(),
607 "The Name"
608 );
609 }
610
611 #[async_test]
612 async fn test_receiving_a_knocked_room_membership_event_creates_a_knocked_room() {
613 let client = logged_in_base_client(None).await;
615 let room_id = room_id!("!r:e.uk");
616 let user_id = client.session_meta().unwrap().user_id.to_owned();
617
618 let mut room = http::response::Room::new();
621 set_room_knocked(&mut room, &user_id);
622
623 let response = response_with_room(room_id, room);
624 client
625 .process_sliding_sync(
626 &response,
627 &RequestedRequiredStates::default(),
628 &client.state_store_lock().lock().await,
629 )
630 .await
631 .expect("Failed to process sync");
632
633 let client_room = client.get_room(room_id).expect("No room found");
635 assert_eq!(client_room.state(), RoomState::Knocked);
636 }
637
638 #[async_test]
639 async fn test_receiving_a_knocked_room_membership_event_with_wrong_state_key_creates_an_invited_room()
640 {
641 let client = logged_in_base_client(None).await;
643 let room_id = room_id!("!r:e.uk");
644 let user_id = user_id!("@w:e.uk");
645
646 let mut room = http::response::Room::new();
648 set_room_knocked(&mut room, user_id);
649
650 let response = response_with_room(room_id, room);
651 client
652 .process_sliding_sync(
653 &response,
654 &RequestedRequiredStates::default(),
655 &client.state_store_lock().lock().await,
656 )
657 .await
658 .expect("Failed to process sync");
659
660 let client_room = client.get_room(room_id).expect("No room found");
663 assert_eq!(client_room.state(), RoomState::Invited);
664 }
665
666 #[async_test]
667 async fn test_receiving_an_unknown_room_membership_event_in_invite_state_creates_an_invited_room()
668 {
669 let client = logged_in_base_client(None).await;
671 let room_id = room_id!("!r:e.uk");
672 let user_id = client.session_meta().unwrap().user_id.to_owned();
673
674 let mut room = http::response::Room::new();
676 let event = Raw::new(&json!({
677 "type": "m.room.member",
678 "sender": user_id,
679 "content": {
680 "is_direct": true,
681 "membership": "join",
682 },
683 "state_key": user_id,
684 }))
685 .expect("Failed to make raw event")
686 .cast_unchecked();
687 room.invite_state = Some(vec![event]);
688
689 let response = response_with_room(room_id, room);
690 client
691 .process_sliding_sync(
692 &response,
693 &RequestedRequiredStates::default(),
694 &client.state_store_lock().lock().await,
695 )
696 .await
697 .expect("Failed to process sync");
698
699 let client_room = client.get_room(room_id).expect("No room found");
701 assert_eq!(client_room.state(), RoomState::Invited);
702 }
703
704 #[async_test]
705 async fn test_left_a_room_from_required_state_event() {
706 let client = logged_in_base_client(None).await;
708 let room_id = room_id!("!r:e.uk");
709 let user_id = user_id!("@u:e.uk");
710
711 let mut room = http::response::Room::new();
713 set_room_joined(&mut room, user_id);
714 let response = response_with_room(room_id, room);
715 client
716 .process_sliding_sync(
717 &response,
718 &RequestedRequiredStates::default(),
719 &client.state_store_lock().lock().await,
720 )
721 .await
722 .expect("Failed to process sync");
723 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Joined);
724
725 let mut room = http::response::Room::new();
727 set_room_left(&mut room, user_id);
728 let response = response_with_room(room_id, room);
729 let sync_resp = client
730 .process_sliding_sync(
731 &response,
732 &RequestedRequiredStates::default(),
733 &client.state_store_lock().lock().await,
734 )
735 .await
736 .expect("Failed to process sync");
737
738 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Left);
740
741 assert!(!sync_resp.rooms.joined.contains_key(room_id));
743 assert!(sync_resp.rooms.left.contains_key(room_id));
744 assert!(!sync_resp.rooms.invited.contains_key(room_id));
745 assert!(!sync_resp.rooms.knocked.contains_key(room_id));
746 }
747
748 #[async_test]
749 async fn test_kick_or_ban_updates_room_to_left() {
750 for membership in [MembershipState::Leave, MembershipState::Ban] {
751 let room_id = room_id!("!r:e.uk");
752 let user_a_id = user_id!("@a:e.uk");
753 let user_b_id = user_id!("@b:e.uk");
754 let client = logged_in_base_client(Some(user_a_id)).await;
755
756 let mut room = http::response::Room::new();
758 set_room_joined(&mut room, user_a_id);
759 let response = response_with_room(room_id, room);
760 client
761 .process_sliding_sync(
762 &response,
763 &RequestedRequiredStates::default(),
764 &client.state_store_lock().lock().await,
765 )
766 .await
767 .expect("Failed to process sync");
768 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Joined);
769
770 let mut room = http::response::Room::new();
772 room.required_state.push(make_state_event(
773 user_b_id,
774 user_a_id.as_str(),
775 RoomMemberEventContent::new(membership.clone()),
776 None,
777 ));
778 let response = response_with_room(room_id, room);
779 let sync_resp = client
780 .process_sliding_sync(
781 &response,
782 &RequestedRequiredStates::default(),
783 &client.state_store_lock().lock().await,
784 )
785 .await
786 .expect("Failed to process sync");
787
788 match membership {
789 MembershipState::Leave => {
790 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Left);
792 }
793 MembershipState::Ban => {
794 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Banned);
796 }
797 _ => panic!("Unexpected membership state found: {membership}"),
798 }
799
800 assert!(!sync_resp.rooms.joined.contains_key(room_id));
802 assert!(sync_resp.rooms.left.contains_key(room_id));
803 assert!(!sync_resp.rooms.invited.contains_key(room_id));
804 assert!(!sync_resp.rooms.knocked.contains_key(room_id));
805 }
806 }
807
808 #[async_test]
809 async fn test_left_a_room_from_timeline_state_event() {
810 let client = logged_in_base_client(None).await;
812 let room_id = room_id!("!r:e.uk");
813 let user_id = user_id!("@u:e.uk");
814
815 let mut room = http::response::Room::new();
817 set_room_joined(&mut room, user_id);
818 let response = response_with_room(room_id, room);
819 client
820 .process_sliding_sync(
821 &response,
822 &RequestedRequiredStates::default(),
823 &client.state_store_lock().lock().await,
824 )
825 .await
826 .expect("Failed to process sync");
827 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Joined);
828
829 let mut room = http::response::Room::new();
831 set_room_left_as_timeline_event(&mut room, user_id);
832 let response = response_with_room(room_id, room);
833 client
834 .process_sliding_sync(
835 &response,
836 &RequestedRequiredStates::default(),
837 &client.state_store_lock().lock().await,
838 )
839 .await
840 .expect("Failed to process sync");
841
842 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Joined);
844 }
845
846 #[async_test]
847 async fn test_can_be_reinvited_to_a_left_room() {
848 let client = logged_in_base_client(None).await;
852 let room_id = room_id!("!r:e.uk");
853 let user_id = user_id!("@u:e.uk");
854
855 let mut room = http::response::Room::new();
857 set_room_joined(&mut room, user_id);
858 let response = response_with_room(room_id, room);
859 client
860 .process_sliding_sync(
861 &response,
862 &RequestedRequiredStates::default(),
863 &client.state_store_lock().lock().await,
864 )
865 .await
866 .expect("Failed to process sync");
867 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Joined);
869
870 let mut room = http::response::Room::new();
872 set_room_left(&mut room, user_id);
873 let response = response_with_room(room_id, room);
874 client
875 .process_sliding_sync(
876 &response,
877 &RequestedRequiredStates::default(),
878 &client.state_store_lock().lock().await,
879 )
880 .await
881 .expect("Failed to process sync");
882 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Left);
884
885 let mut room = http::response::Room::new();
887 set_room_invited(&mut room, user_id, user_id);
888 let response = response_with_room(room_id, room);
889 client
890 .process_sliding_sync(
891 &response,
892 &RequestedRequiredStates::default(),
893 &client.state_store_lock().lock().await,
894 )
895 .await
896 .expect("Failed to process sync");
897
898 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Invited);
900 }
901
902 #[async_test]
903 async fn test_other_person_leaving_a_dm_is_reflected_in_their_membership_and_direct_targets() {
904 let room_id = room_id!("!r:e.uk");
905 let user_a_id = user_id!("@a:e.uk");
906 let user_b_id = user_id!("@b:e.uk");
907
908 let client = logged_in_base_client(None).await;
910 create_dm(&client, room_id, user_a_id, user_b_id, MembershipState::Join).await;
911
912 assert!(
914 direct_targets(&client, room_id).contains(<&DirectUserIdentifier>::from(user_b_id))
915 );
916 assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Join);
917
918 update_room_membership(&client, room_id, user_b_id, MembershipState::Leave).await;
920
921 assert!(
925 direct_targets(&client, room_id).contains(<&DirectUserIdentifier>::from(user_b_id))
926 );
927 assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Leave);
928 }
929
930 #[async_test]
931 async fn test_other_person_refusing_invite_to_a_dm_is_reflected_in_their_membership_and_direct_targets()
932 {
933 let room_id = room_id!("!r:e.uk");
934 let user_a_id = user_id!("@a:e.uk");
935 let user_b_id = user_id!("@b:e.uk");
936
937 let client = logged_in_base_client(None).await;
939 create_dm(&client, room_id, user_a_id, user_b_id, MembershipState::Invite).await;
940
941 assert!(
943 direct_targets(&client, room_id).contains(<&DirectUserIdentifier>::from(user_b_id))
944 );
945 assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Invite);
946
947 update_room_membership(&client, room_id, user_b_id, MembershipState::Leave).await;
949
950 assert!(
954 direct_targets(&client, room_id).contains(<&DirectUserIdentifier>::from(user_b_id))
955 );
956 assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Leave);
957 }
958
959 #[async_test]
960 async fn test_members_count_in_a_dm_where_other_person_has_joined() {
961 let room_id = room_id!("!r:bar.org");
962 let user_a_id = user_id!("@a:bar.org");
963 let user_b_id = user_id!("@b:bar.org");
964
965 let client = logged_in_base_client(None).await;
967 create_dm(&client, room_id, user_a_id, user_b_id, MembershipState::Join).await;
968
969 assert_eq!(membership(&client, room_id, user_a_id).await, MembershipState::Join);
971
972 assert!(
974 direct_targets(&client, room_id).contains(<&DirectUserIdentifier>::from(user_b_id))
975 );
976 assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Join);
977
978 let room = client.get_room(room_id).unwrap();
979
980 assert_eq!(room.active_members_count(), 2);
981 assert_eq!(room.joined_members_count(), 2);
982 assert_eq!(room.invited_members_count(), 0);
983 }
984
985 #[async_test]
986 async fn test_members_count_in_a_dm_where_other_person_is_invited() {
987 let room_id = room_id!("!r:bar.org");
988 let user_a_id = user_id!("@a:bar.org");
989 let user_b_id = user_id!("@b:bar.org");
990
991 let client = logged_in_base_client(None).await;
993 create_dm(&client, room_id, user_a_id, user_b_id, MembershipState::Invite).await;
994
995 assert_eq!(membership(&client, room_id, user_a_id).await, MembershipState::Join);
997
998 assert!(
1000 direct_targets(&client, room_id).contains(<&DirectUserIdentifier>::from(user_b_id))
1001 );
1002 assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Invite);
1003
1004 let room = client.get_room(room_id).unwrap();
1005
1006 assert_eq!(room.active_members_count(), 2);
1007 assert_eq!(room.joined_members_count(), 1);
1008 assert_eq!(room.invited_members_count(), 1);
1009 }
1010
1011 #[async_test]
1012 async fn test_avatar_is_found_when_processing_sliding_sync_response() {
1013 let client = logged_in_base_client(None).await;
1015 let room_id = room_id!("!r:e.uk");
1016
1017 let room = {
1019 let mut room = http::response::Room::new();
1020 room.avatar = JsOption::from_option(Some(owned_mxc_uri!("mxc://e.uk/med1")));
1021
1022 room
1023 };
1024 let response = response_with_room(room_id, room);
1025 client
1026 .process_sliding_sync(
1027 &response,
1028 &RequestedRequiredStates::default(),
1029 &client.state_store_lock().lock().await,
1030 )
1031 .await
1032 .expect("Failed to process sync");
1033
1034 let client_room = client.get_room(room_id).expect("No room found");
1036 assert_eq!(
1037 client_room.avatar_url().expect("No avatar URL").media_id().expect("No media ID"),
1038 "med1"
1039 );
1040 }
1041
1042 #[async_test]
1043 async fn test_avatar_can_be_unset_when_processing_sliding_sync_response() {
1044 let client = logged_in_base_client(None).await;
1046 let room_id = room_id!("!r:e.uk");
1047
1048 let room = {
1052 let mut room = http::response::Room::new();
1053 room.avatar = JsOption::from_option(Some(owned_mxc_uri!("mxc://e.uk/med1")));
1054
1055 room
1056 };
1057 let response = response_with_room(room_id, room);
1058 client
1059 .process_sliding_sync(
1060 &response,
1061 &RequestedRequiredStates::default(),
1062 &client.state_store_lock().lock().await,
1063 )
1064 .await
1065 .expect("Failed to process sync");
1066
1067 let client_room = client.get_room(room_id).expect("No room found");
1069 assert_eq!(
1070 client_room.avatar_url().expect("No avatar URL").media_id().expect("No media ID"),
1071 "med1"
1072 );
1073
1074 let room = http::response::Room::new();
1078 let response = response_with_room(room_id, room);
1079 client
1080 .process_sliding_sync(
1081 &response,
1082 &RequestedRequiredStates::default(),
1083 &client.state_store_lock().lock().await,
1084 )
1085 .await
1086 .expect("Failed to process sync");
1087
1088 let client_room = client.get_room(room_id).expect("No room found");
1090 assert_eq!(
1091 client_room.avatar_url().expect("No avatar URL").media_id().expect("No media ID"),
1092 "med1"
1093 );
1094
1095 let room = {
1099 let mut room = http::response::Room::new();
1100 room.avatar = JsOption::Null;
1101
1102 room
1103 };
1104 let response = response_with_room(room_id, room);
1105 client
1106 .process_sliding_sync(
1107 &response,
1108 &RequestedRequiredStates::default(),
1109 &client.state_store_lock().lock().await,
1110 )
1111 .await
1112 .expect("Failed to process sync");
1113
1114 let client_room = client.get_room(room_id).expect("No room found");
1116 assert!(client_room.avatar_url().is_none());
1117 }
1118
1119 #[async_test]
1120 async fn test_avatar_is_found_from_required_state_when_processing_sliding_sync_response() {
1121 let client = logged_in_base_client(None).await;
1123 let room_id = room_id!("!r:e.uk");
1124 let user_id = user_id!("@u:e.uk");
1125
1126 let room = room_with_avatar(mxc_uri!("mxc://e.uk/med1"), user_id);
1128 let response = response_with_room(room_id, room);
1129 client
1130 .process_sliding_sync(
1131 &response,
1132 &RequestedRequiredStates::default(),
1133 &client.state_store_lock().lock().await,
1134 )
1135 .await
1136 .expect("Failed to process sync");
1137
1138 let client_room = client.get_room(room_id).expect("No room found");
1140 assert_eq!(
1141 client_room.avatar_url().expect("No avatar URL").media_id().expect("No media ID"),
1142 "med1"
1143 );
1144 }
1145
1146 #[async_test]
1147 async fn test_invitation_room_is_added_to_client_and_invite_list() {
1148 let client = logged_in_base_client(None).await;
1150 let room_id = room_id!("!r:e.uk");
1151 let user_id = user_id!("@u:e.uk");
1152
1153 let mut room_info_notable_update = client.room_info_notable_update_receiver();
1154
1155 let mut room = http::response::Room::new();
1157 set_room_invited(&mut room, user_id, user_id);
1158 let response = response_with_room(room_id, room);
1159 let sync_resp = client
1160 .process_sliding_sync(
1161 &response,
1162 &RequestedRequiredStates::default(),
1163 &client.state_store_lock().lock().await,
1164 )
1165 .await
1166 .expect("Failed to process sync");
1167
1168 let client_room = client.get_room(room_id).expect("No room found");
1170 assert_eq!(client_room.room_id(), room_id);
1171 assert_eq!(client_room.state(), RoomState::Invited);
1172
1173 assert!(!sync_resp.rooms.invited[room_id].invite_state.is_empty());
1175 assert!(!sync_resp.rooms.joined.contains_key(room_id));
1176
1177 assert_matches!(
1178 room_info_notable_update.recv().await,
1179 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons }) => {
1180 assert_eq!(received_room_id, room_id);
1181 assert!(reasons.contains(RoomInfoNotableUpdateReasons::MEMBERSHIP));
1183 }
1184 );
1185 assert_matches!(
1186 room_info_notable_update.recv().await,
1187 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons }) => {
1188 assert_eq!(received_room_id, room_id);
1189 assert!(reasons.contains(RoomInfoNotableUpdateReasons::DISPLAY_NAME));
1191 }
1192 );
1193 assert!(room_info_notable_update.is_empty());
1194 }
1195
1196 #[async_test]
1197 async fn test_knock_room_is_added_to_client_and_knock_list() {
1198 let client = logged_in_base_client(None).await;
1200 let room_id = room_id!("!r:e.uk");
1201 let user_id = user_id!("@u:e.uk");
1202
1203 let mut room_info_notable_update = client.room_info_notable_update_receiver();
1204
1205 let mut room = http::response::Room::new();
1207 set_room_knocked(&mut room, user_id);
1208 let response = response_with_room(room_id, room);
1209 let sync_resp = client
1210 .process_sliding_sync(
1211 &response,
1212 &RequestedRequiredStates::default(),
1213 &client.state_store_lock().lock().await,
1214 )
1215 .await
1216 .expect("Failed to process sync");
1217
1218 let client_room = client.get_room(room_id).expect("No room found");
1220 assert_eq!(client_room.room_id(), room_id);
1221 assert_eq!(client_room.state(), RoomState::Knocked);
1222
1223 assert!(!sync_resp.rooms.knocked[room_id].knock_state.is_empty());
1225 assert!(!sync_resp.rooms.joined.contains_key(room_id));
1226
1227 assert_matches!(
1228 room_info_notable_update.recv().await,
1229 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons }) => {
1230 assert_eq!(received_room_id, room_id);
1231 assert!(reasons.contains(RoomInfoNotableUpdateReasons::MEMBERSHIP));
1233 }
1234 );
1235 assert_matches!(
1236 room_info_notable_update.recv().await,
1237 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons }) => {
1238 assert_eq!(received_room_id, room_id);
1239 assert!(reasons.contains(RoomInfoNotableUpdateReasons::DISPLAY_NAME));
1241 }
1242 );
1243 assert!(room_info_notable_update.is_empty());
1244 }
1245
1246 #[async_test]
1247 async fn test_avatar_is_found_in_invitation_room_when_processing_sliding_sync_response() {
1248 let client = logged_in_base_client(None).await;
1250 let room_id = room_id!("!r:e.uk");
1251 let user_id = user_id!("@u:e.uk");
1252
1253 let mut room = room_with_avatar(mxc_uri!("mxc://e.uk/med1"), user_id);
1255 set_room_invited(&mut room, user_id, user_id);
1256 let response = response_with_room(room_id, room);
1257 client
1258 .process_sliding_sync(
1259 &response,
1260 &RequestedRequiredStates::default(),
1261 &client.state_store_lock().lock().await,
1262 )
1263 .await
1264 .expect("Failed to process sync");
1265
1266 let client_room = client.get_room(room_id).expect("No room found");
1268 assert_eq!(
1269 client_room.avatar_url().expect("No avatar URL").media_id().expect("No media ID"),
1270 "med1"
1271 );
1272 }
1273
1274 #[async_test]
1275 async fn test_canonical_alias_is_found_in_invitation_room_when_processing_sliding_sync_response()
1276 {
1277 let client = logged_in_base_client(None).await;
1279 let room_id = room_id!("!r:e.uk");
1280 let user_id = user_id!("@u:e.uk");
1281 let room_alias_id = room_alias_id!("#myroom:e.uk");
1282
1283 let mut room = room_with_canonical_alias(room_alias_id, user_id);
1285 set_room_invited(&mut room, user_id, user_id);
1286 let response = response_with_room(room_id, room);
1287 client
1288 .process_sliding_sync(
1289 &response,
1290 &RequestedRequiredStates::default(),
1291 &client.state_store_lock().lock().await,
1292 )
1293 .await
1294 .expect("Failed to process sync");
1295
1296 let client_room = client.get_room(room_id).expect("No room found");
1298 assert_eq!(client_room.canonical_alias(), Some(room_alias_id.to_owned()));
1299 }
1300
1301 #[async_test]
1302 async fn test_display_name_from_sliding_sync_doesnt_override_alias() {
1303 let client = logged_in_base_client(None).await;
1305 let room_id = room_id!("!r:e.uk");
1306 let user_id = user_id!("@u:e.uk");
1307 let room_alias_id = room_alias_id!("#myroom:e.uk");
1308
1309 let mut room = room_with_canonical_alias(room_alias_id, user_id);
1312 room.name = Some("This came from the server".to_owned());
1313 let response = response_with_room(room_id, room);
1314 client
1315 .process_sliding_sync(
1316 &response,
1317 &RequestedRequiredStates::default(),
1318 &client.state_store_lock().lock().await,
1319 )
1320 .await
1321 .expect("Failed to process sync");
1322
1323 let client_room = client.get_room(room_id).expect("No room found");
1325 assert_eq!(
1326 client_room.compute_display_name().await.unwrap().into_inner().to_string(),
1327 "myroom"
1328 );
1329 assert!(client_room.name().is_none());
1330 }
1331
1332 #[async_test]
1333 async fn test_display_name_is_cached_and_emits_a_notable_update_reason() {
1334 let client = logged_in_base_client(None).await;
1335 let user_id = user_id!("@u:e.uk");
1336 let room_id = room_id!("!r:e.uk");
1337
1338 let mut room_info_notable_update = client.room_info_notable_update_receiver();
1339
1340 let room = room_with_name("Hello World", user_id);
1341 let response = response_with_room(room_id, room);
1342 client
1343 .process_sliding_sync(
1344 &response,
1345 &RequestedRequiredStates::default(),
1346 &client.state_store_lock().lock().await,
1347 )
1348 .await
1349 .expect("Failed to process sync");
1350
1351 let room = client.get_room(room_id).expect("No room found");
1352 assert_eq!(room.cached_display_name().unwrap().to_string(), "Hello World");
1353
1354 assert_matches!(
1355 room_info_notable_update.recv().await,
1356 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons }) => {
1357 assert_eq!(received_room_id, room_id);
1358 assert!(reasons.contains(RoomInfoNotableUpdateReasons::NONE));
1359 }
1360 );
1361 assert_matches!(
1362 room_info_notable_update.recv().await,
1363 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons }) => {
1364 assert_eq!(received_room_id, room_id);
1365 assert!(reasons.contains(RoomInfoNotableUpdateReasons::DISPLAY_NAME));
1367 }
1368 );
1369 assert!(room_info_notable_update.is_empty());
1370 }
1371
1372 #[async_test]
1373 async fn test_display_name_is_persisted_from_sliding_sync() {
1374 let user_id = user_id!("@u:e.uk");
1375 let room_id = room_id!("!r:e.uk");
1376 let session_meta = SessionMeta { user_id: user_id.to_owned(), device_id: "FOOBAR".into() };
1377 let state_store;
1378
1379 {
1380 let client = {
1381 let store = StoreConfig::new(CrossProcessLockConfig::SingleProcess);
1382 state_store = store.state_store.clone();
1383
1384 let client =
1385 BaseClient::new(store, ThreadingSupport::Disabled, DmRoomDefinition::default());
1386 client
1387 .activate(
1388 session_meta.clone(),
1389 RoomLoadSettings::default(),
1390 #[cfg(feature = "e2e-encryption")]
1391 None,
1392 )
1393 .await
1394 .expect("`activate` failed!");
1395
1396 client
1397 };
1398
1399 let room = room_with_name("Hello World", user_id);
1402 let response = response_with_room(room_id, room);
1403 client
1404 .process_sliding_sync(
1405 &response,
1406 &RequestedRequiredStates::default(),
1407 &client.state_store_lock().lock().await,
1408 )
1409 .await
1410 .expect("Failed to process sync");
1411
1412 let room = client.get_room(room_id).expect("No room found");
1413 assert_eq!(room.cached_display_name().unwrap().to_string(), "Hello World");
1414 }
1415
1416 {
1417 let client = {
1418 let mut store = StoreConfig::new(CrossProcessLockConfig::SingleProcess);
1419 store.state_store = state_store;
1420 let client =
1421 BaseClient::new(store, ThreadingSupport::Disabled, DmRoomDefinition::default());
1422 client
1423 .activate(
1424 session_meta,
1425 RoomLoadSettings::default(),
1426 #[cfg(feature = "e2e-encryption")]
1427 None,
1428 )
1429 .await
1430 .expect("`activate` failed!");
1431
1432 client
1433 };
1434
1435 let room = client.get_room(room_id).expect("No room found");
1436 assert_eq!(room.cached_display_name().unwrap().to_string(), "Hello World");
1437 }
1438 }
1439
1440 #[async_test]
1441 async fn test_compute_heroes_from_sliding_sync() {
1442 let client = logged_in_base_client(None).await;
1444 let room_id = room_id!("!r:e.uk");
1445 let gordon = owned_user_id!("@gordon:e.uk");
1446 let alice = owned_user_id!("@alice:e.uk");
1447
1448 let mut room = http::response::Room::new();
1451 room.heroes = Some(vec![
1452 assign!(http::response::Hero::new(gordon), {
1453 name: Some("Gordon".to_owned()),
1454 }),
1455 assign!(http::response::Hero::new(alice), {
1456 name: Some("Alice".to_owned()),
1457 avatar: Some(owned_mxc_uri!("mxc://e.uk/med1"))
1458 }),
1459 ]);
1460 let response = response_with_room(room_id, room);
1461 let _sync_resp = client
1462 .process_sliding_sync(
1463 &response,
1464 &RequestedRequiredStates::default(),
1465 &client.state_store_lock().lock().await,
1466 )
1467 .await
1468 .expect("Failed to process sync");
1469
1470 let client_room = client.get_room(room_id).expect("No room found");
1472 assert_eq!(client_room.room_id(), room_id);
1473 assert_eq!(client_room.state(), RoomState::Joined);
1474
1475 assert_eq!(
1477 client_room.clone_info().summary.heroes(),
1478 &[
1479 RoomHero {
1480 user_id: owned_user_id!("@gordon:e.uk"),
1481 display_name: Some("Gordon".to_owned()),
1482 avatar_url: None
1483 },
1484 RoomHero {
1485 user_id: owned_user_id!("@alice:e.uk"),
1486 display_name: Some("Alice".to_owned()),
1487 avatar_url: Some(owned_mxc_uri!("mxc://e.uk/med1"))
1488 },
1489 ]
1490 );
1491 }
1492
1493 #[async_test]
1494 async fn test_recency_stamp_is_found_when_processing_sliding_sync_response() {
1495 let client = logged_in_base_client(None).await;
1497 let room_id = room_id!("!r:e.uk");
1498
1499 let room = assign!(http::response::Room::new(), {
1501 bump_stamp: Some(42u32.into()),
1502 });
1503 let response = response_with_room(room_id, room);
1504 client
1505 .process_sliding_sync(
1506 &response,
1507 &RequestedRequiredStates::default(),
1508 &client.state_store_lock().lock().await,
1509 )
1510 .await
1511 .expect("Failed to process sync");
1512
1513 let client_room = client.get_room(room_id).expect("No room found");
1515 assert_eq!(client_room.recency_stamp().expect("No recency stamp"), 42.into());
1516 }
1517
1518 #[async_test]
1519 async fn test_recency_stamp_can_be_overwritten_when_present_in_a_sliding_sync_response() {
1520 let client = logged_in_base_client(None).await;
1522 let room_id = room_id!("!r:e.uk");
1523
1524 {
1525 let room = assign!(http::response::Room::new(), {
1527 bump_stamp: Some(42u32.into()),
1528 });
1529 let response = response_with_room(room_id, room);
1530 client
1531 .process_sliding_sync(
1532 &response,
1533 &RequestedRequiredStates::default(),
1534 &client.state_store_lock().lock().await,
1535 )
1536 .await
1537 .expect("Failed to process sync");
1538
1539 let client_room = client.get_room(room_id).expect("No room found");
1541 assert_eq!(client_room.recency_stamp().expect("No recency stamp"), 42.into());
1542 }
1543
1544 {
1545 let room = assign!(http::response::Room::new(), {
1547 bump_stamp: None,
1548 });
1549 let response = response_with_room(room_id, room);
1550 client
1551 .process_sliding_sync(
1552 &response,
1553 &RequestedRequiredStates::default(),
1554 &client.state_store_lock().lock().await,
1555 )
1556 .await
1557 .expect("Failed to process sync");
1558
1559 let client_room = client.get_room(room_id).expect("No room found");
1561 assert_eq!(client_room.recency_stamp().expect("No recency stamp"), 42.into());
1562 }
1563
1564 {
1565 let room = assign!(http::response::Room::new(), {
1568 bump_stamp: Some(153u32.into()),
1569 });
1570 let response = response_with_room(room_id, room);
1571 client
1572 .process_sliding_sync(
1573 &response,
1574 &RequestedRequiredStates::default(),
1575 &client.state_store_lock().lock().await,
1576 )
1577 .await
1578 .expect("Failed to process sync");
1579
1580 let client_room = client.get_room(room_id).expect("No room found");
1582 assert_eq!(client_room.recency_stamp().expect("No recency stamp"), 153.into());
1583 }
1584 }
1585
1586 #[async_test]
1587 async fn test_recency_stamp_can_trigger_a_notable_update_reason() {
1588 let client = logged_in_base_client(None).await;
1590 let mut room_info_notable_update_stream = client.room_info_notable_update_receiver();
1591 let room_id = room_id!("!r:e.uk");
1592
1593 let room = assign!(http::response::Room::new(), {
1595 bump_stamp: Some(42u32.into()),
1596 });
1597 let response = response_with_room(room_id, room);
1598 client
1599 .process_sliding_sync(
1600 &response,
1601 &RequestedRequiredStates::default(),
1602 &client.state_store_lock().lock().await,
1603 )
1604 .await
1605 .expect("Failed to process sync");
1606
1607 assert_matches!(
1610 room_info_notable_update_stream.recv().await,
1611 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1612 assert_eq!(received_room_id, room_id);
1613 assert!(!received_reasons.contains(RoomInfoNotableUpdateReasons::RECENCY_STAMP));
1614 }
1615 );
1616 assert_matches!(
1617 room_info_notable_update_stream.recv().await,
1618 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1619 assert_eq!(received_room_id, room_id);
1620 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::DISPLAY_NAME));
1621 }
1622 );
1623 assert!(room_info_notable_update_stream.is_empty());
1624
1625 let room = assign!(http::response::Room::new(), {
1627 bump_stamp: Some(43u32.into()),
1628 });
1629 let response = response_with_room(room_id, room);
1630 client
1631 .process_sliding_sync(
1632 &response,
1633 &RequestedRequiredStates::default(),
1634 &client.state_store_lock().lock().await,
1635 )
1636 .await
1637 .expect("Failed to process sync");
1638
1639 assert_matches!(
1641 room_info_notable_update_stream.recv().await,
1642 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1643 assert_eq!(received_room_id, room_id);
1644 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::RECENCY_STAMP));
1645 }
1646 );
1647 assert!(room_info_notable_update_stream.is_empty());
1648 }
1649
1650 #[async_test]
1651 async fn test_leaving_room_can_trigger_a_notable_update_reason() {
1652 let client = logged_in_base_client(None).await;
1654 let mut room_info_notable_update_stream = client.room_info_notable_update_receiver();
1655
1656 let room_id = room_id!("!r:e.uk");
1658 let room = http::response::Room::new();
1659 let response = response_with_room(room_id, room);
1660 client
1661 .process_sliding_sync(
1662 &response,
1663 &RequestedRequiredStates::default(),
1664 &client.state_store_lock().lock().await,
1665 )
1666 .await
1667 .expect("Failed to process sync");
1668
1669 assert_matches!(
1671 room_info_notable_update_stream.recv().await,
1672 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1673 assert_eq!(received_room_id, room_id);
1674 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::NONE));
1675 }
1676 );
1677 assert_matches!(
1678 room_info_notable_update_stream.recv().await,
1679 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1680 assert_eq!(received_room_id, room_id);
1681 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::DISPLAY_NAME));
1682 }
1683 );
1684
1685 let room_id = room_id!("!r:e.uk");
1687 let events = vec![
1688 Raw::from_json_string(
1689 json!({
1690 "type": "m.room.member",
1691 "event_id": "$3",
1692 "content": { "membership": "join" },
1693 "sender": "@u:h.uk",
1694 "origin_server_ts": 12344445,
1695 "state_key": "@u:e.uk",
1696 })
1697 .to_string(),
1698 )
1699 .unwrap(),
1700 ];
1701 let room = assign!(http::response::Room::new(), {
1702 required_state: events,
1703 });
1704 let response = response_with_room(room_id, room);
1705 client
1706 .process_sliding_sync(
1707 &response,
1708 &RequestedRequiredStates::default(),
1709 &client.state_store_lock().lock().await,
1710 )
1711 .await
1712 .expect("Failed to process sync");
1713
1714 assert_matches!(
1716 room_info_notable_update_stream.recv().await,
1717 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1718 assert_eq!(received_room_id, room_id);
1719 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::NONE));
1720 }
1721 );
1722 assert!(room_info_notable_update_stream.is_empty());
1723
1724 let events = vec![
1725 Raw::from_json_string(
1726 json!({
1727 "type": "m.room.member",
1728 "event_id": "$3",
1729 "content": { "membership": "leave" },
1730 "sender": "@u:h.uk",
1731 "origin_server_ts": 12344445,
1732 "state_key": "@u:e.uk",
1733 })
1734 .to_string(),
1735 )
1736 .unwrap(),
1737 ];
1738 let room = assign!(http::response::Room::new(), {
1739 required_state: events,
1740 });
1741 let response = response_with_room(room_id, room);
1742 client
1743 .process_sliding_sync(
1744 &response,
1745 &RequestedRequiredStates::default(),
1746 &client.state_store_lock().lock().await,
1747 )
1748 .await
1749 .expect("Failed to process sync");
1750
1751 assert_matches!(
1753 room_info_notable_update_stream.recv().await,
1754 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1755 assert_eq!(received_room_id, room_id);
1756 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::MEMBERSHIP));
1757 }
1758 );
1759 assert!(room_info_notable_update_stream.is_empty());
1760 }
1761
1762 #[async_test]
1763 async fn test_unread_marker_can_trigger_a_notable_update_reason() {
1764 let client = logged_in_base_client(None).await;
1766 let mut room_info_notable_update_stream = client.room_info_notable_update_receiver();
1767
1768 let room_id = room_id!("!r:e.uk");
1770 let room = http::response::Room::new();
1771 let response = response_with_room(room_id, room);
1772 client
1773 .process_sliding_sync(
1774 &response,
1775 &RequestedRequiredStates::default(),
1776 &client.state_store_lock().lock().await,
1777 )
1778 .await
1779 .expect("Failed to process sync");
1780
1781 assert_matches!(
1783 room_info_notable_update_stream.recv().await,
1784 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1785 assert_eq!(received_room_id, room_id);
1786 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::NONE), "{received_reasons:?}");
1787 }
1788 );
1789 assert_matches!(
1790 room_info_notable_update_stream.recv().await,
1791 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1792 assert_eq!(received_room_id, room_id);
1793 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::DISPLAY_NAME), "{received_reasons:?}");
1794 }
1795 );
1796 assert!(room_info_notable_update_stream.is_empty());
1797
1798 let room_id = room_id!("!r:e.uk");
1801 let room_account_data_events = vec![
1802 Raw::from_json_string(
1803 json!({
1804 "type": "m.marked_unread",
1805 "event_id": "$1",
1806 "content": { "unread": true },
1807 "sender": client.session_meta().unwrap().user_id,
1808 "origin_server_ts": 12344445,
1809 })
1810 .to_string(),
1811 )
1812 .unwrap(),
1813 ];
1814 let mut response = response_with_room(room_id, http::response::Room::new());
1815 response.extensions.account_data.rooms.insert(room_id.to_owned(), room_account_data_events);
1816
1817 client
1818 .process_sliding_sync(
1819 &response,
1820 &RequestedRequiredStates::default(),
1821 &client.state_store_lock().lock().await,
1822 )
1823 .await
1824 .expect("Failed to process sync");
1825
1826 assert_matches!(
1828 room_info_notable_update_stream.recv().await,
1829 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1830 assert_eq!(received_room_id, room_id);
1831 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::UNREAD_MARKER), "{received_reasons:?}");
1832 }
1833 );
1834
1835 client
1837 .process_sliding_sync(
1838 &response,
1839 &RequestedRequiredStates::default(),
1840 &client.state_store_lock().lock().await,
1841 )
1842 .await
1843 .expect("Failed to process sync");
1844
1845 assert_matches!(
1846 room_info_notable_update_stream.recv().await,
1847 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1848 assert_eq!(received_room_id, room_id);
1849 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::NONE), "{received_reasons:?}");
1850 }
1851 );
1852 assert!(room_info_notable_update_stream.is_empty());
1853
1854 let room_account_data_events = vec![
1856 Raw::from_json_string(
1857 json!({
1858 "type": "m.marked_unread",
1859 "event_id": "$1",
1860 "content": { "unread": false },
1861 "sender": client.session_meta().unwrap().user_id,
1862 "origin_server_ts": 12344445,
1863 })
1864 .to_string(),
1865 )
1866 .unwrap(),
1867 ];
1868 response.extensions.account_data.rooms.insert(room_id.to_owned(), room_account_data_events);
1869 client
1870 .process_sliding_sync(
1871 &response,
1872 &RequestedRequiredStates::default(),
1873 &client.state_store_lock().lock().await,
1874 )
1875 .await
1876 .expect("Failed to process sync");
1877
1878 assert_matches!(
1879 room_info_notable_update_stream.recv().await,
1880 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1881 assert_eq!(received_room_id, room_id);
1882 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::UNREAD_MARKER));
1883 }
1884 );
1885 assert!(room_info_notable_update_stream.is_empty());
1886 }
1887
1888 #[async_test]
1889 async fn test_fully_read_marker_can_trigger_a_notable_update_reason() {
1890 let client = logged_in_base_client(None).await;
1892 let mut room_info_notable_update_stream = client.room_info_notable_update_receiver();
1893
1894 let room_id = room_id!("!r:e.uk");
1896 let room = http::response::Room::new();
1897 let response = response_with_room(room_id, room);
1898 client
1899 .process_sliding_sync(
1900 &response,
1901 &RequestedRequiredStates::default(),
1902 &client.state_store_lock().lock().await,
1903 )
1904 .await
1905 .expect("Failed to process sync");
1906
1907 assert_matches!(
1909 room_info_notable_update_stream.recv().await,
1910 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1911 assert_eq!(received_room_id, room_id);
1912 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::NONE), "{received_reasons:?}");
1913 }
1914 );
1915 assert_matches!(
1916 room_info_notable_update_stream.recv().await,
1917 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1918 assert_eq!(received_room_id, room_id);
1919 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::DISPLAY_NAME), "{received_reasons:?}");
1920 }
1921 );
1922 assert!(room_info_notable_update_stream.is_empty());
1923
1924 let room_account_data_events = vec![
1927 Raw::from_json_string(
1928 json!({
1929 "type": "m.fully_read",
1930 "content": { "event_id": "$first" },
1931 })
1932 .to_string(),
1933 )
1934 .unwrap(),
1935 ];
1936 let mut response = response_with_room(room_id, http::response::Room::new());
1937 response.extensions.account_data.rooms.insert(room_id.to_owned(), room_account_data_events);
1938
1939 client
1940 .process_sliding_sync(
1941 &response,
1942 &RequestedRequiredStates::default(),
1943 &client.state_store_lock().lock().await,
1944 )
1945 .await
1946 .expect("Failed to process sync");
1947
1948 assert_matches!(
1950 room_info_notable_update_stream.recv().await,
1951 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1952 assert_eq!(received_room_id, room_id);
1953 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::FULLY_READ), "{received_reasons:?}");
1954 }
1955 );
1956
1957 let room = client.get_room(room_id).expect("room should exist");
1958 assert_eq!(room.fully_read_event_id().as_deref().map(|id| id.as_str()), Some("$first"),);
1959
1960 client
1962 .process_sliding_sync(
1963 &response,
1964 &RequestedRequiredStates::default(),
1965 &client.state_store_lock().lock().await,
1966 )
1967 .await
1968 .expect("Failed to process sync");
1969
1970 assert_matches!(
1971 room_info_notable_update_stream.recv().await,
1972 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1973 assert_eq!(received_room_id, room_id);
1974 assert!(!received_reasons.contains(RoomInfoNotableUpdateReasons::FULLY_READ), "{received_reasons:?}");
1975 }
1976 );
1977 assert!(room_info_notable_update_stream.is_empty());
1978
1979 let room_account_data_events = vec![
1981 Raw::from_json_string(
1982 json!({
1983 "type": "m.fully_read",
1984 "content": { "event_id": "$second" },
1985 })
1986 .to_string(),
1987 )
1988 .unwrap(),
1989 ];
1990 response.extensions.account_data.rooms.insert(room_id.to_owned(), room_account_data_events);
1991 client
1992 .process_sliding_sync(
1993 &response,
1994 &RequestedRequiredStates::default(),
1995 &client.state_store_lock().lock().await,
1996 )
1997 .await
1998 .expect("Failed to process sync");
1999
2000 assert_matches!(
2001 room_info_notable_update_stream.recv().await,
2002 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
2003 assert_eq!(received_room_id, room_id);
2004 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::FULLY_READ), "{received_reasons:?}");
2005 }
2006 );
2007 assert_eq!(room.fully_read_event_id().as_deref().map(|id| id.as_str()), Some("$second"),);
2008 assert!(room_info_notable_update_stream.is_empty());
2009 }
2010
2011 #[async_test]
2012 async fn test_unstable_unread_marker_is_ignored_after_stable() {
2013 let client = logged_in_base_client(None).await;
2015 let mut room_info_notable_update_stream = client.room_info_notable_update_receiver();
2016
2017 let room_id = room_id!("!r:e.uk");
2019 let room = http::response::Room::new();
2020 let response = response_with_room(room_id, room);
2021 client
2022 .process_sliding_sync(
2023 &response,
2024 &RequestedRequiredStates::default(),
2025 &client.state_store_lock().lock().await,
2026 )
2027 .await
2028 .expect("Failed to process sync");
2029
2030 assert_matches!(
2032 room_info_notable_update_stream.recv().await,
2033 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
2034 assert_eq!(received_room_id, room_id);
2035 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::NONE), "{received_reasons:?}");
2036 }
2037 );
2038 assert_matches!(
2039 room_info_notable_update_stream.recv().await,
2040 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
2041 assert_eq!(received_room_id, room_id);
2042 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::DISPLAY_NAME), "{received_reasons:?}");
2043 }
2044 );
2045 assert!(room_info_notable_update_stream.is_empty());
2046
2047 let room_id = room_id!("!r:e.uk");
2050 let unstable_room_account_data_events = vec![
2051 Raw::from_json_string(
2052 json!({
2053 "type": "com.famedly.marked_unread",
2054 "event_id": "$1",
2055 "content": { "unread": true },
2056 "sender": client.session_meta().unwrap().user_id,
2057 "origin_server_ts": 12344445,
2058 })
2059 .to_string(),
2060 )
2061 .unwrap(),
2062 ];
2063 let mut response = response_with_room(room_id, http::response::Room::new());
2064 response
2065 .extensions
2066 .account_data
2067 .rooms
2068 .insert(room_id.to_owned(), unstable_room_account_data_events.clone());
2069
2070 client
2071 .process_sliding_sync(
2072 &response,
2073 &RequestedRequiredStates::default(),
2074 &client.state_store_lock().lock().await,
2075 )
2076 .await
2077 .expect("Failed to process sync");
2078
2079 assert_matches!(
2081 room_info_notable_update_stream.recv().await,
2082 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
2083 assert_eq!(received_room_id, room_id);
2084 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::UNREAD_MARKER), "{received_reasons:?}");
2085 }
2086 );
2087 assert!(room_info_notable_update_stream.is_empty());
2088
2089 let stable_room_account_data_events = vec![
2091 Raw::from_json_string(
2092 json!({
2093 "type": "m.marked_unread",
2094 "event_id": "$1",
2095 "content": { "unread": false },
2096 "sender": client.session_meta().unwrap().user_id,
2097 "origin_server_ts": 12344445,
2098 })
2099 .to_string(),
2100 )
2101 .unwrap(),
2102 ];
2103 response
2104 .extensions
2105 .account_data
2106 .rooms
2107 .insert(room_id.to_owned(), stable_room_account_data_events);
2108 client
2109 .process_sliding_sync(
2110 &response,
2111 &RequestedRequiredStates::default(),
2112 &client.state_store_lock().lock().await,
2113 )
2114 .await
2115 .expect("Failed to process sync");
2116
2117 assert_matches!(
2119 room_info_notable_update_stream.recv().await,
2120 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
2121 assert_eq!(received_room_id, room_id);
2122 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::UNREAD_MARKER));
2123 }
2124 );
2125 assert!(room_info_notable_update_stream.is_empty());
2126
2127 response
2130 .extensions
2131 .account_data
2132 .rooms
2133 .insert(room_id.to_owned(), unstable_room_account_data_events);
2134 client
2135 .process_sliding_sync(
2136 &response,
2137 &RequestedRequiredStates::default(),
2138 &client.state_store_lock().lock().await,
2139 )
2140 .await
2141 .expect("Failed to process sync");
2142
2143 assert_matches!(
2145 room_info_notable_update_stream.recv().await,
2146 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
2147 assert_eq!(received_room_id, room_id);
2148 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::NONE), "{received_reasons:?}");
2149 }
2150 );
2151 assert!(room_info_notable_update_stream.is_empty());
2152
2153 let stable_room_account_data_events = vec![
2156 Raw::from_json_string(
2157 json!({
2158 "type": "m.marked_unread",
2159 "event_id": "$3",
2160 "content": { "unread": true },
2161 "sender": client.session_meta().unwrap().user_id,
2162 "origin_server_ts": 12344445,
2163 })
2164 .to_string(),
2165 )
2166 .unwrap(),
2167 ];
2168 response
2169 .extensions
2170 .account_data
2171 .rooms
2172 .insert(room_id.to_owned(), stable_room_account_data_events);
2173 client
2174 .process_sliding_sync(
2175 &response,
2176 &RequestedRequiredStates::default(),
2177 &client.state_store_lock().lock().await,
2178 )
2179 .await
2180 .expect("Failed to process sync");
2181
2182 assert_matches!(
2184 room_info_notable_update_stream.recv().await,
2185 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
2186 assert_eq!(received_room_id, room_id);
2187 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::UNREAD_MARKER));
2188 }
2189 );
2190 assert!(room_info_notable_update_stream.is_empty());
2191 }
2192
2193 #[async_test]
2194 async fn test_pinned_events_are_updated_on_sync() {
2195 let user_a_id = user_id!("@a:e.uk");
2196 let client = logged_in_base_client(Some(user_a_id)).await;
2197 let room_id = room_id!("!r:e.uk");
2198 let pinned_event_id = owned_event_id!("$an-id:e.uk");
2199
2200 let mut room_response = http::response::Room::new();
2202 set_room_joined(&mut room_response, user_a_id);
2203 let response = response_with_room(room_id, room_response);
2204 client
2205 .process_sliding_sync(
2206 &response,
2207 &RequestedRequiredStates::default(),
2208 &client.state_store_lock().lock().await,
2209 )
2210 .await
2211 .expect("Failed to process sync");
2212
2213 let room = client.get_room(room_id).unwrap();
2215 let pinned_event_ids = room.pinned_event_ids();
2216 assert_matches!(pinned_event_ids, None);
2217
2218 let mut room_response = http::response::Room::new();
2220 room_response.required_state.push(make_state_event(
2221 user_a_id,
2222 "",
2223 RoomPinnedEventsEventContent::new(vec![pinned_event_id.clone()]),
2224 None,
2225 ));
2226 let response = response_with_room(room_id, room_response);
2227 client
2228 .process_sliding_sync(
2229 &response,
2230 &RequestedRequiredStates::default(),
2231 &client.state_store_lock().lock().await,
2232 )
2233 .await
2234 .expect("Failed to process sync");
2235
2236 let pinned_event_ids = room.pinned_event_ids().unwrap_or_default();
2237 assert_eq!(pinned_event_ids.len(), 1);
2238 assert_eq!(pinned_event_ids[0], pinned_event_id);
2239
2240 let mut room_response = http::response::Room::new();
2242 room_response.required_state.push(make_state_event(
2243 user_a_id,
2244 "",
2245 RoomPinnedEventsEventContent::new(Vec::new()),
2246 None,
2247 ));
2248 let response = response_with_room(room_id, room_response);
2249 client
2250 .process_sliding_sync(
2251 &response,
2252 &RequestedRequiredStates::default(),
2253 &client.state_store_lock().lock().await,
2254 )
2255 .await
2256 .expect("Failed to process sync");
2257 let pinned_event_ids = room.pinned_event_ids().unwrap();
2258 assert!(pinned_event_ids.is_empty());
2259 }
2260
2261 #[async_test]
2262 async fn test_dms_are_processed_in_any_sync_response() {
2263 let current_user_id = user_id!("@current:e.uk");
2264 let client = logged_in_base_client(Some(current_user_id)).await;
2265 let user_a_id = user_id!("@a:e.uk");
2266 let user_b_id = user_id!("@b:e.uk");
2267 let room_id_1 = room_id!("!r:e.uk");
2268 let room_id_2 = room_id!("!s:e.uk");
2269
2270 let mut room_response = http::response::Room::new();
2271 set_room_joined(&mut room_response, user_a_id);
2272 let mut response = response_with_room(room_id_1, room_response);
2273 let mut direct_content: BTreeMap<OwnedDirectUserIdentifier, Vec<OwnedRoomId>> =
2274 BTreeMap::new();
2275 direct_content.insert(user_a_id.into(), vec![room_id_1.to_owned()]);
2276 direct_content.insert(user_b_id.into(), vec![room_id_2.to_owned()]);
2277 response
2278 .extensions
2279 .account_data
2280 .global
2281 .push(make_global_account_data_event(DirectEventContent(direct_content)));
2282 client
2283 .process_sliding_sync(
2284 &response,
2285 &RequestedRequiredStates::default(),
2286 &client.state_store_lock().lock().await,
2287 )
2288 .await
2289 .expect("Failed to process sync");
2290
2291 let room_1 = client.get_room(room_id_1).unwrap();
2292 assert!(room_1.is_direct().await.unwrap());
2293
2294 let mut room_response = http::response::Room::new();
2296 set_room_joined(&mut room_response, user_b_id);
2297 let response = response_with_room(room_id_2, room_response);
2298 client
2299 .process_sliding_sync(
2300 &response,
2301 &RequestedRequiredStates::default(),
2302 &client.state_store_lock().lock().await,
2303 )
2304 .await
2305 .expect("Failed to process sync");
2306
2307 let room_2 = client.get_room(room_id_2).unwrap();
2308 assert!(room_2.is_direct().await.unwrap());
2309 }
2310
2311 #[async_test]
2312 async fn test_room_encryption_state_is_and_is_not_encrypted() {
2313 let user_id = user_id!("@raclette:patate");
2314 let client = logged_in_base_client(Some(user_id)).await;
2315 let room_id_0 = room_id!("!r0");
2316 let room_id_1 = room_id!("!r1");
2317 let room_id_2 = room_id!("!r2");
2318
2319 let requested_required_states = RequestedRequiredStates::from(&{
2336 let mut request = http::Request::new();
2337
2338 request.room_subscriptions.insert(room_id_0.to_owned(), {
2339 let mut room_subscription = http::request::RoomSubscription::default();
2340
2341 room_subscription
2342 .required_state
2343 .push((StateEventType::RoomEncryption, "".to_owned()));
2344
2345 room_subscription
2346 });
2347
2348 request
2349 });
2350
2351 let mut response = http::Response::new("0".to_owned());
2352
2353 {
2357 let not_encrypted_room = http::response::Room::new();
2358 let mut encrypted_room = http::response::Room::new();
2359 set_room_is_encrypted(&mut encrypted_room, user_id);
2360
2361 response.rooms.insert(room_id_0.to_owned(), encrypted_room.clone());
2362 response.rooms.insert(room_id_1.to_owned(), encrypted_room);
2363 response.rooms.insert(room_id_2.to_owned(), not_encrypted_room);
2364 }
2365
2366 client
2367 .process_sliding_sync(
2368 &response,
2369 &requested_required_states,
2370 &client.state_store_lock().lock().await,
2371 )
2372 .await
2373 .expect("Failed to process sync");
2374
2375 assert_matches!(
2377 client.get_room(room_id_0).unwrap().encryption_state(),
2378 EncryptionState::Encrypted
2379 );
2380 assert_matches!(
2381 client.get_room(room_id_1).unwrap().encryption_state(),
2382 EncryptionState::Encrypted
2383 );
2384 assert_matches!(
2386 client.get_room(room_id_2).unwrap().encryption_state(),
2387 EncryptionState::NotEncrypted
2388 )
2389 }
2390
2391 #[async_test]
2392 async fn test_room_encryption_state_is_unknown() {
2393 let user_id = user_id!("@raclette:patate");
2394 let client = logged_in_base_client(Some(user_id)).await;
2395 let room_id_0 = room_id!("!r0");
2396 let room_id_1 = room_id!("!r1");
2397
2398 let requested_required_states = RequestedRequiredStates::from(&http::Request::new());
2411
2412 let mut response = http::Response::new("0".to_owned());
2413
2414 {
2416 let not_encrypted_room = http::response::Room::new();
2417 let mut encrypted_room = http::response::Room::new();
2418 set_room_is_encrypted(&mut encrypted_room, user_id);
2419
2420 response.rooms.insert(room_id_0.to_owned(), encrypted_room);
2421 response.rooms.insert(room_id_1.to_owned(), not_encrypted_room);
2422 }
2423
2424 client
2425 .process_sliding_sync(
2426 &response,
2427 &requested_required_states,
2428 &client.state_store_lock().lock().await,
2429 )
2430 .await
2431 .expect("Failed to process sync");
2432
2433 assert_matches!(
2436 client.get_room(room_id_0).unwrap().encryption_state(),
2437 EncryptionState::Encrypted
2438 );
2439 assert_matches!(
2442 client.get_room(room_id_1).unwrap().encryption_state(),
2443 EncryptionState::Unknown
2444 );
2445 }
2446
2447 async fn membership(
2448 client: &BaseClient,
2449 room_id: &RoomId,
2450 user_id: &UserId,
2451 ) -> MembershipState {
2452 let room = client.get_room(room_id).expect("Room not found!");
2453 let member = room.get_member(user_id).await.unwrap().expect("B not in room");
2454 member.membership().clone()
2455 }
2456
2457 fn direct_targets(client: &BaseClient, room_id: &RoomId) -> HashSet<OwnedDirectUserIdentifier> {
2458 let room = client.get_room(room_id).expect("Room not found!");
2459 room.direct_targets()
2460 }
2461
2462 async fn create_dm(
2465 client: &BaseClient,
2466 room_id: &RoomId,
2467 my_id: &UserId,
2468 their_id: &UserId,
2469 other_state: MembershipState,
2470 ) {
2471 let mut room = http::response::Room::new();
2472 set_room_joined(&mut room, my_id);
2473
2474 match other_state {
2475 MembershipState::Join => {
2476 room.joined_count = Some(uint!(2));
2477 room.invited_count = None;
2478 }
2479
2480 MembershipState::Invite => {
2481 room.joined_count = Some(uint!(1));
2482 room.invited_count = Some(uint!(1));
2483 }
2484
2485 _ => {
2486 room.joined_count = Some(uint!(1));
2487 room.invited_count = None;
2488 }
2489 }
2490
2491 room.required_state.push(make_membership_event(their_id, other_state));
2492
2493 let mut response = response_with_room(room_id, room);
2494 set_direct_with(&mut response, their_id.to_owned(), vec![room_id.to_owned()]);
2495 client
2496 .process_sliding_sync(
2497 &response,
2498 &RequestedRequiredStates::default(),
2499 &client.state_store_lock().lock().await,
2500 )
2501 .await
2502 .expect("Failed to process sync");
2503 }
2504
2505 async fn update_room_membership(
2507 client: &BaseClient,
2508 room_id: &RoomId,
2509 user_id: &UserId,
2510 new_state: MembershipState,
2511 ) {
2512 let mut room = http::response::Room::new();
2513 room.required_state.push(make_membership_event(user_id, new_state));
2514 let response = response_with_room(room_id, room);
2515 client
2516 .process_sliding_sync(
2517 &response,
2518 &RequestedRequiredStates::default(),
2519 &client.state_store_lock().lock().await,
2520 )
2521 .await
2522 .expect("Failed to process sync");
2523 }
2524
2525 fn set_direct_with(
2526 response: &mut http::Response,
2527 user_id: OwnedUserId,
2528 room_ids: Vec<OwnedRoomId>,
2529 ) {
2530 let mut direct_content: BTreeMap<OwnedDirectUserIdentifier, Vec<OwnedRoomId>> =
2531 BTreeMap::new();
2532 direct_content.insert(user_id.into(), room_ids);
2533 response
2534 .extensions
2535 .account_data
2536 .global
2537 .push(make_global_account_data_event(DirectEventContent(direct_content)));
2538 }
2539
2540 fn response_with_room(room_id: &RoomId, room: http::response::Room) -> http::Response {
2541 let mut response = http::Response::new("5".to_owned());
2542 response.rooms.insert(room_id.to_owned(), room);
2543 response
2544 }
2545
2546 fn room_with_avatar(avatar_uri: &MxcUri, user_id: &UserId) -> http::response::Room {
2547 let mut room = http::response::Room::new();
2548
2549 let mut avatar_event_content = RoomAvatarEventContent::new();
2550 avatar_event_content.url = Some(avatar_uri.to_owned());
2551
2552 room.required_state.push(make_state_event(user_id, "", avatar_event_content, None));
2553
2554 room
2555 }
2556
2557 fn room_with_canonical_alias(
2558 room_alias_id: &RoomAliasId,
2559 user_id: &UserId,
2560 ) -> http::response::Room {
2561 let mut room = http::response::Room::new();
2562
2563 let mut canonical_alias_event_content = RoomCanonicalAliasEventContent::new();
2564 canonical_alias_event_content.alias = Some(room_alias_id.to_owned());
2565
2566 room.required_state.push(make_state_event(
2567 user_id,
2568 "",
2569 canonical_alias_event_content,
2570 None,
2571 ));
2572
2573 room
2574 }
2575
2576 fn room_with_name(name: &str, user_id: &UserId) -> http::response::Room {
2577 let mut room = http::response::Room::new();
2578
2579 let name_event_content = RoomNameEventContent::new(name.to_owned());
2580
2581 room.required_state.push(make_state_event(user_id, "", name_event_content, None));
2582
2583 room
2584 }
2585
2586 fn set_room_name(room: &mut http::response::Room, sender: &UserId, name: String) {
2587 room.required_state.push(make_state_event(
2588 sender,
2589 "",
2590 RoomNameEventContent::new(name),
2591 None,
2592 ));
2593 }
2594
2595 fn set_room_invited(room: &mut http::response::Room, inviter: &UserId, invitee: &UserId) {
2596 let evt = Raw::new(&json!({
2600 "type": "m.room.member",
2601 "sender": inviter,
2602 "content": {
2603 "is_direct": true,
2604 "membership": "invite",
2605 },
2606 "state_key": invitee,
2607 }))
2608 .expect("Failed to make raw event")
2609 .cast_unchecked();
2610
2611 room.invite_state = Some(vec![evt]);
2612
2613 room.required_state.push(make_state_event(
2616 inviter,
2617 invitee.as_str(),
2618 RoomMemberEventContent::new(MembershipState::Invite),
2619 None,
2620 ));
2621 }
2622
2623 fn set_room_knocked(room: &mut http::response::Room, knocker: &UserId) {
2624 let evt = Raw::new(&json!({
2628 "type": "m.room.member",
2629 "sender": knocker,
2630 "content": {
2631 "is_direct": true,
2632 "membership": "knock",
2633 },
2634 "state_key": knocker,
2635 }))
2636 .expect("Failed to make raw event")
2637 .cast_unchecked();
2638
2639 room.invite_state = Some(vec![evt]);
2640 }
2641
2642 fn set_room_joined(room: &mut http::response::Room, user_id: &UserId) {
2643 room.required_state.push(make_membership_event(user_id, MembershipState::Join));
2644 }
2645
2646 fn set_room_left(room: &mut http::response::Room, user_id: &UserId) {
2647 room.required_state.push(make_membership_event(user_id, MembershipState::Leave));
2648 }
2649
2650 fn set_room_left_as_timeline_event(room: &mut http::response::Room, user_id: &UserId) {
2651 room.timeline.push(make_membership_event(user_id, MembershipState::Leave));
2652 }
2653
2654 fn set_room_is_encrypted(room: &mut http::response::Room, user_id: &UserId) {
2655 room.required_state.push(make_encryption_event(user_id));
2656 }
2657
2658 fn make_membership_event<K>(user_id: &UserId, state: MembershipState) -> Raw<K> {
2659 make_state_event(user_id, user_id.as_str(), RoomMemberEventContent::new(state), None)
2660 }
2661
2662 fn make_encryption_event<K>(user_id: &UserId) -> Raw<K> {
2663 make_state_event(user_id, "", RoomEncryptionEventContent::with_recommended_defaults(), None)
2664 }
2665
2666 fn make_global_account_data_event<C: GlobalAccountDataEventContent, E>(content: C) -> Raw<E> {
2667 Raw::new(&json!({
2668 "type": content.event_type(),
2669 "content": content,
2670 }))
2671 .expect("Failed to create account data event")
2672 .cast_unchecked()
2673 }
2674
2675 fn make_state_event<C: StateEventContent, E>(
2676 sender: &UserId,
2677 state_key: &str,
2678 content: C,
2679 prev_content: Option<C>,
2680 ) -> Raw<E> {
2681 let unsigned = if let Some(prev_content) = prev_content {
2682 json!({ "prev_content": prev_content })
2683 } else {
2684 json!({})
2685 };
2686
2687 Raw::new(&json!({
2688 "type": content.event_type(),
2689 "state_key": state_key,
2690 "content": content,
2691 "event_id": event_id!("$evt"),
2692 "sender": sender,
2693 "origin_server_ts": 10,
2694 "unsigned": unsigned,
2695 }))
2696 .expect("Failed to create state event")
2697 .cast_unchecked()
2698 }
2699}