1#[cfg(feature = "e2e-encryption")]
18use matrix_sdk_common::deserialized_responses::ProcessedToDeviceEvent;
19use matrix_sdk_common::deserialized_responses::TimelineEvent;
20use ruma::{api::client::sync::sync_events::v5 as http, OwnedRoomId};
21use tracing::{instrument, trace};
22
23use super::BaseClient;
24use crate::{
25 error::Result,
26 read_receipts::compute_unread_counts,
27 response_processors as processors,
28 room::RoomInfoNotableUpdateReasons,
29 store::ambiguity_map::AmbiguityCache,
30 sync::{RoomUpdates, SyncResponse},
31 RequestedRequiredStates,
32};
33
34impl BaseClient {
35 #[cfg(feature = "e2e-encryption")]
43 pub async fn process_sliding_sync_e2ee(
44 &self,
45 to_device: Option<&http::response::ToDevice>,
46 e2ee: &http::response::E2EE,
47 ) -> Result<Option<Vec<ProcessedToDeviceEvent>>> {
48 if to_device.is_none() && e2ee.is_empty() {
49 return Ok(None);
50 }
51
52 trace!(
53 to_device_events =
54 to_device.map(|to_device| to_device.events.len()).unwrap_or_default(),
55 device_one_time_keys_count = e2ee.device_one_time_keys_count.len(),
56 device_unused_fallback_key_types =
57 e2ee.device_unused_fallback_key_types.as_ref().map(|v| v.len()),
58 "Processing sliding sync e2ee events",
59 );
60
61 let olm_machine = self.olm_machine().await;
62
63 let mut context = processors::Context::default();
64
65 let processors::e2ee::to_device::Output { processed_to_device_events, room_key_updates } =
66 processors::e2ee::to_device::from_msc4186(to_device, e2ee, olm_machine.as_ref())
67 .await?;
68
69 processors::latest_event::decrypt_from_rooms(
70 &mut context,
71 room_key_updates
72 .into_iter()
73 .flatten()
74 .filter_map(|room_key_info| self.get_room(&room_key_info.room_id))
75 .collect(),
76 processors::e2ee::E2EE::new(
77 olm_machine.as_ref(),
78 &self.decryption_settings,
79 self.handle_verification_events,
80 ),
81 )
82 .await?;
83
84 processors::changes::save_and_apply(
85 context,
86 &self.state_store,
87 &self.ignore_user_list_changes,
88 None,
89 )
90 .await?;
91
92 Ok(Some(processed_to_device_events))
93 }
94
95 #[instrument(skip_all, level = "trace")]
102 pub async fn process_sliding_sync(
103 &self,
104 response: &http::Response,
105 requested_required_states: &RequestedRequiredStates,
106 ) -> Result<SyncResponse> {
107 let http::Response { rooms, lists, extensions, .. } = response;
108
109 trace!(
110 rooms = rooms.len(),
111 lists = lists.len(),
112 has_extensions = !extensions.is_empty(),
113 "Processing sliding sync room events"
114 );
115
116 if rooms.is_empty() && extensions.is_empty() {
117 return Ok(SyncResponse::default());
120 }
121
122 let mut context = processors::Context::default();
123
124 let state_store = self.state_store.clone();
125 let mut ambiguity_cache = AmbiguityCache::new(state_store.inner.clone());
126
127 let global_account_data_processor =
128 processors::account_data::global(&extensions.account_data.global);
129 let push_rules = self.get_push_rules(&global_account_data_processor).await?;
130
131 let mut room_updates = RoomUpdates::default();
132 let mut notifications = Default::default();
133
134 let user_id = self
135 .session_meta()
136 .expect("Sliding sync shouldn't run without an authenticated user.")
137 .user_id
138 .to_owned();
139
140 for (room_id, room_response) in rooms {
141 let Some((room_info, room_update)) = processors::room::msc4186::update_any_room(
142 &mut context,
143 &user_id,
144 processors::room::RoomCreationData::new(
145 room_id,
146 self.room_info_notable_update_sender.clone(),
147 requested_required_states,
148 &mut ambiguity_cache,
149 ),
150 room_response,
151 &extensions.account_data.rooms,
152 #[cfg(feature = "e2e-encryption")]
153 processors::e2ee::E2EE::new(
154 self.olm_machine().await.as_ref(),
155 &self.decryption_settings,
156 self.handle_verification_events,
157 ),
158 processors::notification::Notification::new(
159 &push_rules,
160 &mut notifications,
161 &self.state_store,
162 ),
163 )
164 .await?
165 else {
166 continue;
167 };
168
169 context.state_changes.add_room(room_info);
170
171 let room_id = room_id.to_owned();
172
173 use processors::room::msc4186::RoomUpdateKind;
174
175 match room_update {
176 RoomUpdateKind::Joined(joined_room_update) => {
177 room_updates.joined.insert(room_id, joined_room_update);
178 }
179 RoomUpdateKind::Left(left_room_update) => {
180 room_updates.left.insert(room_id, left_room_update);
181 }
182 RoomUpdateKind::Invited(invited_room_update) => {
183 room_updates.invited.insert(room_id, invited_room_update);
184 }
185 RoomUpdateKind::Knocked(knocked_room_update) => {
186 room_updates.knocked.insert(room_id, knocked_room_update);
187 }
188 }
189 }
190
191 processors::room::msc4186::extensions::dispatch_typing_ephemeral_events(
195 &extensions.typing,
196 &mut room_updates.joined,
197 );
198
199 processors::room::msc4186::extensions::room_account_data(
201 &mut context,
202 &extensions.account_data,
203 &mut room_updates,
204 &self.state_store,
205 )
206 .await;
207
208 global_account_data_processor.apply(&mut context, &state_store).await;
209
210 context.state_changes.ambiguity_maps = ambiguity_cache.cache;
211
212 processors::changes::save_and_apply(
214 context,
215 &self.state_store,
216 &self.ignore_user_list_changes,
217 None,
218 )
219 .await?;
220
221 let mut context = processors::Context::default();
222
223 processors::room::display_name::update_for_rooms(
226 &mut context,
227 &room_updates,
228 &self.state_store,
229 )
230 .await;
231
232 processors::changes::save_only(context, &self.state_store).await?;
234
235 Ok(SyncResponse {
236 rooms: room_updates,
237 notifications,
238 presence: Default::default(),
239 account_data: extensions.account_data.global.clone(),
240 to_device: Default::default(),
241 })
242 }
243
244 #[doc(hidden)]
247 pub async fn process_sliding_sync_receipts_extension_for_room(
248 &self,
249 room_id: &OwnedRoomId,
250 response: &http::Response,
251 sync_response: &mut SyncResponse,
252 room_previous_events: Vec<TimelineEvent>,
253 ) -> Result<()> {
254 let mut context = processors::Context::default();
255
256 let mut save_context = false;
257
258 let joined_room_update = sync_response.rooms.joined.entry(room_id.to_owned()).or_default();
261
262 if let Some(receipt_ephemeral_event) = response.extensions.receipts.rooms.get(room_id) {
264 processors::room::msc4186::extensions::dispatch_receipt_ephemeral_event_for_room(
265 &mut context,
266 room_id,
267 receipt_ephemeral_event,
268 joined_room_update,
269 );
270 save_context = true;
271 }
272
273 let user_id = &self.session_meta().expect("logged in user").user_id;
274
275 if let Some(mut room_info) = self.get_room(room_id).map(|room| room.clone_info()) {
278 let prev_read_receipts = room_info.read_receipts.clone();
279
280 compute_unread_counts(
281 user_id,
282 room_id,
283 context.state_changes.receipts.get(room_id),
284 room_previous_events,
285 &joined_room_update.timeline.events,
286 &mut room_info.read_receipts,
287 self.threading_support,
288 );
289
290 if prev_read_receipts != room_info.read_receipts {
291 context
292 .room_info_notable_updates
293 .entry(room_id.clone())
294 .or_default()
295 .insert(RoomInfoNotableUpdateReasons::READ_RECEIPT);
296
297 context.state_changes.add_room(room_info);
298 save_context = true;
299 }
300 }
301
302 if save_context {
304 processors::changes::save_only(context, &self.state_store).await?;
305 }
306
307 Ok(())
308 }
309}
310
311#[cfg(all(test, not(target_family = "wasm")))]
312mod tests {
313 use std::collections::{BTreeMap, HashSet};
314 #[cfg(feature = "e2e-encryption")]
315 use std::sync::{Arc, RwLock as SyncRwLock};
316
317 use assert_matches::assert_matches;
318 use matrix_sdk_common::deserialized_responses::TimelineEvent;
319 #[cfg(feature = "e2e-encryption")]
320 use matrix_sdk_common::{
321 deserialized_responses::{UnableToDecryptInfo, UnableToDecryptReason},
322 ring_buffer::RingBuffer,
323 };
324 use matrix_sdk_test::async_test;
325 use ruma::{
326 api::client::sync::sync_events::UnreadNotificationsCount,
327 assign, event_id,
328 events::{
329 direct::{DirectEventContent, DirectUserIdentifier, OwnedDirectUserIdentifier},
330 room::{
331 avatar::RoomAvatarEventContent,
332 canonical_alias::RoomCanonicalAliasEventContent,
333 encryption::RoomEncryptionEventContent,
334 member::{MembershipState, RoomMemberEventContent},
335 message::SyncRoomMessageEvent,
336 name::RoomNameEventContent,
337 pinned_events::RoomPinnedEventsEventContent,
338 },
339 AnySyncMessageLikeEvent, AnySyncTimelineEvent, GlobalAccountDataEventContent,
340 StateEventContent, StateEventType,
341 },
342 mxc_uri, owned_event_id, owned_mxc_uri, owned_user_id, room_alias_id, room_id,
343 serde::Raw,
344 uint, user_id, JsOption, MxcUri, OwnedRoomId, OwnedUserId, RoomAliasId, RoomId, UserId,
345 };
346 use serde_json::json;
347
348 use super::http;
349 #[cfg(feature = "e2e-encryption")]
350 use super::processors::room::msc4186::cache_latest_events;
351 use crate::{
352 client::ThreadingSupport,
353 room::{RoomHero, RoomInfoNotableUpdateReasons},
354 store::{RoomLoadSettings, StoreConfig},
355 test_utils::logged_in_base_client,
356 BaseClient, EncryptionState, RequestedRequiredStates, RoomInfoNotableUpdate, RoomState,
357 SessionMeta,
358 };
359 #[cfg(feature = "e2e-encryption")]
360 use crate::{store::MemoryStore, Room};
361
362 #[async_test]
363 async fn test_notification_count_set() {
364 let client = logged_in_base_client(None).await;
365
366 let mut response = http::Response::new("42".to_owned());
367 let room_id = room_id!("!room:example.org");
368 let count = assign!(UnreadNotificationsCount::default(), {
369 highlight_count: Some(uint!(13)),
370 notification_count: Some(uint!(37)),
371 });
372
373 response.rooms.insert(
374 room_id.to_owned(),
375 assign!(http::response::Room::new(), {
376 unread_notifications: count.clone()
377 }),
378 );
379
380 let sync_response = client
381 .process_sliding_sync(&response, &RequestedRequiredStates::default())
382 .await
383 .expect("Failed to process sync");
384
385 let room = sync_response.rooms.joined.get(room_id).unwrap();
387 assert_eq!(room.unread_notifications, count.clone().into());
388
389 let room = client.get_room(room_id).expect("found room");
391 assert_eq!(room.unread_notification_counts(), count.into());
392 }
393
394 #[async_test]
395 async fn test_can_process_empty_sliding_sync_response() {
396 let client = logged_in_base_client(None).await;
397 let empty_response = http::Response::new("5".to_owned());
398 client
399 .process_sliding_sync(&empty_response, &RequestedRequiredStates::default())
400 .await
401 .expect("Failed to process sync");
402 }
403
404 #[async_test]
405 async fn test_room_with_unspecified_state_is_added_to_client_and_joined_list() {
406 let client = logged_in_base_client(None).await;
408 let room_id = room_id!("!r:e.uk");
409
410 let mut room = http::response::Room::new();
413 room.joined_count = Some(uint!(41));
414 let response = response_with_room(room_id, room);
415 let sync_resp = client
416 .process_sliding_sync(&response, &RequestedRequiredStates::default())
417 .await
418 .expect("Failed to process sync");
419
420 let client_room = client.get_room(room_id).expect("No room found");
422 assert_eq!(client_room.room_id(), room_id);
423 assert_eq!(client_room.joined_members_count(), 41);
424 assert_eq!(client_room.state(), RoomState::Joined);
425
426 assert!(sync_resp.rooms.joined.contains_key(room_id));
428 assert!(!sync_resp.rooms.left.contains_key(room_id));
429 assert!(!sync_resp.rooms.invited.contains_key(room_id));
430 }
431
432 #[async_test]
433 async fn test_missing_room_name_event() {
434 let client = logged_in_base_client(None).await;
436 let room_id = room_id!("!r:e.uk");
437
438 let mut room = http::response::Room::new();
441 room.name = Some("little room".to_owned());
442 let response = response_with_room(room_id, room);
443 let sync_resp = client
444 .process_sliding_sync(&response, &RequestedRequiredStates::default())
445 .await
446 .expect("Failed to process sync");
447
448 let client_room = client.get_room(room_id).expect("No room found");
450 assert!(client_room.name().is_none());
451 assert_eq!(
452 client_room.compute_display_name().await.unwrap().into_inner().to_string(),
453 "Empty Room"
454 );
455 assert_eq!(client_room.state(), RoomState::Joined);
456
457 assert!(sync_resp.rooms.joined.contains_key(room_id));
459 assert!(!sync_resp.rooms.left.contains_key(room_id));
460 assert!(!sync_resp.rooms.invited.contains_key(room_id));
461 assert!(!sync_resp.rooms.knocked.contains_key(room_id));
462 }
463
464 #[async_test]
465 async fn test_room_name_event() {
466 let client = logged_in_base_client(None).await;
468 let room_id = room_id!("!r:e.uk");
469
470 let mut room = http::response::Room::new();
473
474 room.name = Some("little room".to_owned());
475 set_room_name(&mut room, user_id!("@a:b.c"), "The Name".to_owned());
476
477 let response = response_with_room(room_id, room);
478 client
479 .process_sliding_sync(&response, &RequestedRequiredStates::default())
480 .await
481 .expect("Failed to process sync");
482
483 let client_room = client.get_room(room_id).expect("No room found");
485 assert_eq!(client_room.name().as_deref(), Some("The Name"));
486 assert_eq!(
487 client_room.compute_display_name().await.unwrap().into_inner().to_string(),
488 "The Name"
489 );
490 }
491
492 #[async_test]
493 async fn test_missing_invited_room_name_event() {
494 let client = logged_in_base_client(None).await;
496 let room_id = room_id!("!r:e.uk");
497 let user_id = user_id!("@w:e.uk");
498 let inviter = user_id!("@john:mastodon.org");
499
500 let mut room = http::response::Room::new();
503 set_room_invited(&mut room, inviter, user_id);
504 room.name = Some("name from sliding sync response".to_owned());
505 let response = response_with_room(room_id, room);
506 let sync_resp = client
507 .process_sliding_sync(&response, &RequestedRequiredStates::default())
508 .await
509 .expect("Failed to process sync");
510
511 let client_room = client.get_room(room_id).expect("No room found");
513 assert!(client_room.name().is_none());
514
515 assert_eq!(client_room.compute_display_name().await.unwrap().into_inner().to_string(), "w");
517
518 assert_eq!(client_room.state(), RoomState::Invited);
519
520 assert!(!sync_resp.rooms.joined.contains_key(room_id));
522 assert!(!sync_resp.rooms.left.contains_key(room_id));
523 assert!(sync_resp.rooms.invited.contains_key(room_id));
524 assert!(!sync_resp.rooms.knocked.contains_key(room_id));
525 }
526
527 #[async_test]
528 async fn test_invited_room_name_event() {
529 let client = logged_in_base_client(None).await;
531 let room_id = room_id!("!r:e.uk");
532 let user_id = user_id!("@w:e.uk");
533 let inviter = user_id!("@john:mastodon.org");
534
535 let mut room = http::response::Room::new();
538
539 set_room_invited(&mut room, inviter, user_id);
540
541 room.name = Some("name from sliding sync response".to_owned());
542 set_room_name(&mut room, user_id!("@a:b.c"), "The Name".to_owned());
543
544 let response = response_with_room(room_id, room);
545 client
546 .process_sliding_sync(&response, &RequestedRequiredStates::default())
547 .await
548 .expect("Failed to process sync");
549
550 let client_room = client.get_room(room_id).expect("No room found");
552 assert_eq!(client_room.name().as_deref(), Some("The Name"));
553 assert_eq!(
554 client_room.compute_display_name().await.unwrap().into_inner().to_string(),
555 "The Name"
556 );
557 }
558
559 #[async_test]
560 async fn test_receiving_a_knocked_room_membership_event_creates_a_knocked_room() {
561 let client = logged_in_base_client(None).await;
563 let room_id = room_id!("!r:e.uk");
564 let user_id = client.session_meta().unwrap().user_id.to_owned();
565
566 let mut room = http::response::Room::new();
569 set_room_knocked(&mut room, &user_id);
570
571 let response = response_with_room(room_id, room);
572 client
573 .process_sliding_sync(&response, &RequestedRequiredStates::default())
574 .await
575 .expect("Failed to process sync");
576
577 let client_room = client.get_room(room_id).expect("No room found");
579 assert_eq!(client_room.state(), RoomState::Knocked);
580 }
581
582 #[async_test]
583 async fn test_receiving_a_knocked_room_membership_event_with_wrong_state_key_creates_an_invited_room(
584 ) {
585 let client = logged_in_base_client(None).await;
587 let room_id = room_id!("!r:e.uk");
588 let user_id = user_id!("@w:e.uk");
589
590 let mut room = http::response::Room::new();
592 set_room_knocked(&mut room, user_id);
593
594 let response = response_with_room(room_id, room);
595 client
596 .process_sliding_sync(&response, &RequestedRequiredStates::default())
597 .await
598 .expect("Failed to process sync");
599
600 let client_room = client.get_room(room_id).expect("No room found");
603 assert_eq!(client_room.state(), RoomState::Invited);
604 }
605
606 #[async_test]
607 async fn test_receiving_an_unknown_room_membership_event_in_invite_state_creates_an_invited_room(
608 ) {
609 let client = logged_in_base_client(None).await;
611 let room_id = room_id!("!r:e.uk");
612 let user_id = client.session_meta().unwrap().user_id.to_owned();
613
614 let mut room = http::response::Room::new();
616 let event = Raw::new(&json!({
617 "type": "m.room.member",
618 "sender": user_id,
619 "content": {
620 "is_direct": true,
621 "membership": "join",
622 },
623 "state_key": user_id,
624 }))
625 .expect("Failed to make raw event")
626 .cast();
627 room.invite_state = Some(vec![event]);
628
629 let response = response_with_room(room_id, room);
630 client
631 .process_sliding_sync(&response, &RequestedRequiredStates::default())
632 .await
633 .expect("Failed to process sync");
634
635 let client_room = client.get_room(room_id).expect("No room found");
637 assert_eq!(client_room.state(), RoomState::Invited);
638 }
639
640 #[async_test]
641 async fn test_left_a_room_from_required_state_event() {
642 let client = logged_in_base_client(None).await;
644 let room_id = room_id!("!r:e.uk");
645 let user_id = user_id!("@u:e.uk");
646
647 let mut room = http::response::Room::new();
649 set_room_joined(&mut room, user_id);
650 let response = response_with_room(room_id, room);
651 client
652 .process_sliding_sync(&response, &RequestedRequiredStates::default())
653 .await
654 .expect("Failed to process sync");
655 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Joined);
656
657 let mut room = http::response::Room::new();
659 set_room_left(&mut room, user_id);
660 let response = response_with_room(room_id, room);
661 let sync_resp = client
662 .process_sliding_sync(&response, &RequestedRequiredStates::default())
663 .await
664 .expect("Failed to process sync");
665
666 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Left);
668
669 assert!(!sync_resp.rooms.joined.contains_key(room_id));
671 assert!(sync_resp.rooms.left.contains_key(room_id));
672 assert!(!sync_resp.rooms.invited.contains_key(room_id));
673 assert!(!sync_resp.rooms.knocked.contains_key(room_id));
674 }
675
676 #[async_test]
677 async fn test_kick_or_ban_updates_room_to_left() {
678 for membership in [MembershipState::Leave, MembershipState::Ban] {
679 let room_id = room_id!("!r:e.uk");
680 let user_a_id = user_id!("@a:e.uk");
681 let user_b_id = user_id!("@b:e.uk");
682 let client = logged_in_base_client(Some(user_a_id)).await;
683
684 let mut room = http::response::Room::new();
686 set_room_joined(&mut room, user_a_id);
687 let response = response_with_room(room_id, room);
688 client
689 .process_sliding_sync(&response, &RequestedRequiredStates::default())
690 .await
691 .expect("Failed to process sync");
692 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Joined);
693
694 let mut room = http::response::Room::new();
696 room.required_state.push(make_state_event(
697 user_b_id,
698 user_a_id.as_str(),
699 RoomMemberEventContent::new(membership.clone()),
700 None,
701 ));
702 let response = response_with_room(room_id, room);
703 let sync_resp = client
704 .process_sliding_sync(&response, &RequestedRequiredStates::default())
705 .await
706 .expect("Failed to process sync");
707
708 match membership {
709 MembershipState::Leave => {
710 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Left);
712 }
713 MembershipState::Ban => {
714 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Banned);
716 }
717 _ => panic!("Unexpected membership state found: {membership}"),
718 }
719
720 assert!(!sync_resp.rooms.joined.contains_key(room_id));
722 assert!(sync_resp.rooms.left.contains_key(room_id));
723 assert!(!sync_resp.rooms.invited.contains_key(room_id));
724 assert!(!sync_resp.rooms.knocked.contains_key(room_id));
725 }
726 }
727
728 #[async_test]
729 async fn test_left_a_room_from_timeline_state_event() {
730 let client = logged_in_base_client(None).await;
732 let room_id = room_id!("!r:e.uk");
733 let user_id = user_id!("@u:e.uk");
734
735 let mut room = http::response::Room::new();
737 set_room_joined(&mut room, user_id);
738 let response = response_with_room(room_id, room);
739 client
740 .process_sliding_sync(&response, &RequestedRequiredStates::default())
741 .await
742 .expect("Failed to process sync");
743 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Joined);
744
745 let mut room = http::response::Room::new();
747 set_room_left_as_timeline_event(&mut room, user_id);
748 let response = response_with_room(room_id, room);
749 client
750 .process_sliding_sync(&response, &RequestedRequiredStates::default())
751 .await
752 .expect("Failed to process sync");
753
754 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Joined);
756 }
757
758 #[async_test]
759 async fn test_can_be_reinvited_to_a_left_room() {
760 let client = logged_in_base_client(None).await;
764 let room_id = room_id!("!r:e.uk");
765 let user_id = user_id!("@u:e.uk");
766
767 let mut room = http::response::Room::new();
769 set_room_joined(&mut room, user_id);
770 let response = response_with_room(room_id, room);
771 client
772 .process_sliding_sync(&response, &RequestedRequiredStates::default())
773 .await
774 .expect("Failed to process sync");
775 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Joined);
777
778 let mut room = http::response::Room::new();
780 set_room_left(&mut room, user_id);
781 let response = response_with_room(room_id, room);
782 client
783 .process_sliding_sync(&response, &RequestedRequiredStates::default())
784 .await
785 .expect("Failed to process sync");
786 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Left);
788
789 let mut room = http::response::Room::new();
791 set_room_invited(&mut room, user_id, user_id);
792 let response = response_with_room(room_id, room);
793 client
794 .process_sliding_sync(&response, &RequestedRequiredStates::default())
795 .await
796 .expect("Failed to process sync");
797
798 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Invited);
800 }
801
802 #[async_test]
803 async fn test_other_person_leaving_a_dm_is_reflected_in_their_membership_and_direct_targets() {
804 let room_id = room_id!("!r:e.uk");
805 let user_a_id = user_id!("@a:e.uk");
806 let user_b_id = user_id!("@b:e.uk");
807
808 let client = logged_in_base_client(None).await;
810 create_dm(&client, room_id, user_a_id, user_b_id, MembershipState::Join).await;
811
812 assert!(direct_targets(&client, room_id).contains(<&DirectUserIdentifier>::from(user_b_id)));
814 assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Join);
815
816 update_room_membership(&client, room_id, user_b_id, MembershipState::Leave).await;
818
819 assert!(direct_targets(&client, room_id).contains(<&DirectUserIdentifier>::from(user_b_id)));
823 assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Leave);
824 }
825
826 #[async_test]
827 async fn test_other_person_refusing_invite_to_a_dm_is_reflected_in_their_membership_and_direct_targets(
828 ) {
829 let room_id = room_id!("!r:e.uk");
830 let user_a_id = user_id!("@a:e.uk");
831 let user_b_id = user_id!("@b:e.uk");
832
833 let client = logged_in_base_client(None).await;
835 create_dm(&client, room_id, user_a_id, user_b_id, MembershipState::Invite).await;
836
837 assert!(direct_targets(&client, room_id).contains(<&DirectUserIdentifier>::from(user_b_id)));
839 assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Invite);
840
841 update_room_membership(&client, room_id, user_b_id, MembershipState::Leave).await;
843
844 assert!(direct_targets(&client, room_id).contains(<&DirectUserIdentifier>::from(user_b_id)));
848 assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Leave);
849 }
850
851 #[async_test]
852 async fn test_members_count_in_a_dm_where_other_person_has_joined() {
853 let room_id = room_id!("!r:bar.org");
854 let user_a_id = user_id!("@a:bar.org");
855 let user_b_id = user_id!("@b:bar.org");
856
857 let client = logged_in_base_client(None).await;
859 create_dm(&client, room_id, user_a_id, user_b_id, MembershipState::Join).await;
860
861 assert_eq!(membership(&client, room_id, user_a_id).await, MembershipState::Join);
863
864 assert!(direct_targets(&client, room_id).contains(<&DirectUserIdentifier>::from(user_b_id)));
866 assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Join);
867
868 let room = client.get_room(room_id).unwrap();
869
870 assert_eq!(room.active_members_count(), 2);
871 assert_eq!(room.joined_members_count(), 2);
872 assert_eq!(room.invited_members_count(), 0);
873 }
874
875 #[async_test]
876 async fn test_members_count_in_a_dm_where_other_person_is_invited() {
877 let room_id = room_id!("!r:bar.org");
878 let user_a_id = user_id!("@a:bar.org");
879 let user_b_id = user_id!("@b:bar.org");
880
881 let client = logged_in_base_client(None).await;
883 create_dm(&client, room_id, user_a_id, user_b_id, MembershipState::Invite).await;
884
885 assert_eq!(membership(&client, room_id, user_a_id).await, MembershipState::Join);
887
888 assert!(direct_targets(&client, room_id).contains(<&DirectUserIdentifier>::from(user_b_id)));
890 assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Invite);
891
892 let room = client.get_room(room_id).unwrap();
893
894 assert_eq!(room.active_members_count(), 2);
895 assert_eq!(room.joined_members_count(), 1);
896 assert_eq!(room.invited_members_count(), 1);
897 }
898
899 #[async_test]
900 async fn test_avatar_is_found_when_processing_sliding_sync_response() {
901 let client = logged_in_base_client(None).await;
903 let room_id = room_id!("!r:e.uk");
904
905 let room = {
907 let mut room = http::response::Room::new();
908 room.avatar = JsOption::from_option(Some(mxc_uri!("mxc://e.uk/med1").to_owned()));
909
910 room
911 };
912 let response = response_with_room(room_id, room);
913 client
914 .process_sliding_sync(&response, &RequestedRequiredStates::default())
915 .await
916 .expect("Failed to process sync");
917
918 let client_room = client.get_room(room_id).expect("No room found");
920 assert_eq!(
921 client_room.avatar_url().expect("No avatar URL").media_id().expect("No media ID"),
922 "med1"
923 );
924 }
925
926 #[async_test]
927 async fn test_avatar_can_be_unset_when_processing_sliding_sync_response() {
928 let client = logged_in_base_client(None).await;
930 let room_id = room_id!("!r:e.uk");
931
932 let room = {
936 let mut room = http::response::Room::new();
937 room.avatar = JsOption::from_option(Some(mxc_uri!("mxc://e.uk/med1").to_owned()));
938
939 room
940 };
941 let response = response_with_room(room_id, room);
942 client
943 .process_sliding_sync(&response, &RequestedRequiredStates::default())
944 .await
945 .expect("Failed to process sync");
946
947 let client_room = client.get_room(room_id).expect("No room found");
949 assert_eq!(
950 client_room.avatar_url().expect("No avatar URL").media_id().expect("No media ID"),
951 "med1"
952 );
953
954 let room = http::response::Room::new();
958 let response = response_with_room(room_id, room);
959 client
960 .process_sliding_sync(&response, &RequestedRequiredStates::default())
961 .await
962 .expect("Failed to process sync");
963
964 let client_room = client.get_room(room_id).expect("No room found");
966 assert_eq!(
967 client_room.avatar_url().expect("No avatar URL").media_id().expect("No media ID"),
968 "med1"
969 );
970
971 let room = {
975 let mut room = http::response::Room::new();
976 room.avatar = JsOption::Null;
977
978 room
979 };
980 let response = response_with_room(room_id, room);
981 client
982 .process_sliding_sync(&response, &RequestedRequiredStates::default())
983 .await
984 .expect("Failed to process sync");
985
986 let client_room = client.get_room(room_id).expect("No room found");
988 assert!(client_room.avatar_url().is_none());
989 }
990
991 #[async_test]
992 async fn test_avatar_is_found_from_required_state_when_processing_sliding_sync_response() {
993 let client = logged_in_base_client(None).await;
995 let room_id = room_id!("!r:e.uk");
996 let user_id = user_id!("@u:e.uk");
997
998 let room = room_with_avatar(mxc_uri!("mxc://e.uk/med1"), user_id);
1000 let response = response_with_room(room_id, room);
1001 client
1002 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1003 .await
1004 .expect("Failed to process sync");
1005
1006 let client_room = client.get_room(room_id).expect("No room found");
1008 assert_eq!(
1009 client_room.avatar_url().expect("No avatar URL").media_id().expect("No media ID"),
1010 "med1"
1011 );
1012 }
1013
1014 #[async_test]
1015 async fn test_invitation_room_is_added_to_client_and_invite_list() {
1016 let client = logged_in_base_client(None).await;
1018 let room_id = room_id!("!r:e.uk");
1019 let user_id = user_id!("@u:e.uk");
1020
1021 let mut room = http::response::Room::new();
1023 set_room_invited(&mut room, user_id, user_id);
1024 let response = response_with_room(room_id, room);
1025 let sync_resp = client
1026 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1027 .await
1028 .expect("Failed to process sync");
1029
1030 let client_room = client.get_room(room_id).expect("No room found");
1032 assert_eq!(client_room.room_id(), room_id);
1033 assert_eq!(client_room.state(), RoomState::Invited);
1034
1035 assert!(!sync_resp.rooms.invited[room_id].invite_state.is_empty());
1037 assert!(!sync_resp.rooms.joined.contains_key(room_id));
1038 }
1039
1040 #[async_test]
1041 async fn test_avatar_is_found_in_invitation_room_when_processing_sliding_sync_response() {
1042 let client = logged_in_base_client(None).await;
1044 let room_id = room_id!("!r:e.uk");
1045 let user_id = user_id!("@u:e.uk");
1046
1047 let mut room = room_with_avatar(mxc_uri!("mxc://e.uk/med1"), user_id);
1049 set_room_invited(&mut room, user_id, user_id);
1050 let response = response_with_room(room_id, room);
1051 client
1052 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1053 .await
1054 .expect("Failed to process sync");
1055
1056 let client_room = client.get_room(room_id).expect("No room found");
1058 assert_eq!(
1059 client_room.avatar_url().expect("No avatar URL").media_id().expect("No media ID"),
1060 "med1"
1061 );
1062 }
1063
1064 #[async_test]
1065 async fn test_canonical_alias_is_found_in_invitation_room_when_processing_sliding_sync_response(
1066 ) {
1067 let client = logged_in_base_client(None).await;
1069 let room_id = room_id!("!r:e.uk");
1070 let user_id = user_id!("@u:e.uk");
1071 let room_alias_id = room_alias_id!("#myroom:e.uk");
1072
1073 let mut room = room_with_canonical_alias(room_alias_id, user_id);
1075 set_room_invited(&mut room, user_id, user_id);
1076 let response = response_with_room(room_id, room);
1077 client
1078 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1079 .await
1080 .expect("Failed to process sync");
1081
1082 let client_room = client.get_room(room_id).expect("No room found");
1084 assert_eq!(client_room.canonical_alias(), Some(room_alias_id.to_owned()));
1085 }
1086
1087 #[async_test]
1088 async fn test_display_name_from_sliding_sync_doesnt_override_alias() {
1089 let client = logged_in_base_client(None).await;
1091 let room_id = room_id!("!r:e.uk");
1092 let user_id = user_id!("@u:e.uk");
1093 let room_alias_id = room_alias_id!("#myroom:e.uk");
1094
1095 let mut room = room_with_canonical_alias(room_alias_id, user_id);
1098 room.name = Some("This came from the server".to_owned());
1099 let response = response_with_room(room_id, room);
1100 client
1101 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1102 .await
1103 .expect("Failed to process sync");
1104
1105 let client_room = client.get_room(room_id).expect("No room found");
1107 assert_eq!(
1108 client_room.compute_display_name().await.unwrap().into_inner().to_string(),
1109 "myroom"
1110 );
1111 assert!(client_room.name().is_none());
1112 }
1113
1114 #[async_test]
1115 async fn test_display_name_is_cached_and_emits_a_notable_update_reason() {
1116 let client = logged_in_base_client(None).await;
1117 let user_id = user_id!("@u:e.uk");
1118 let room_id = room_id!("!r:e.uk");
1119
1120 let mut room_info_notable_update = client.room_info_notable_update_receiver();
1121
1122 let room = room_with_name("Hello World", user_id);
1123 let response = response_with_room(room_id, room);
1124 client
1125 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1126 .await
1127 .expect("Failed to process sync");
1128
1129 let room = client.get_room(room_id).expect("No room found");
1130 assert_eq!(room.cached_display_name().unwrap().to_string(), "Hello World");
1131
1132 assert_matches!(
1133 room_info_notable_update.recv().await,
1134 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons }) => {
1135 assert_eq!(received_room_id, room_id);
1136 assert!(reasons.contains(RoomInfoNotableUpdateReasons::NONE));
1137 }
1138 );
1139 assert_matches!(
1140 room_info_notable_update.recv().await,
1141 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons }) => {
1142 assert_eq!(received_room_id, room_id);
1143 assert!(reasons.contains(RoomInfoNotableUpdateReasons::DISPLAY_NAME));
1145 }
1146 );
1147 assert!(room_info_notable_update.is_empty());
1148 }
1149
1150 #[async_test]
1151 async fn test_display_name_is_persisted_from_sliding_sync() {
1152 let user_id = user_id!("@u:e.uk");
1153 let room_id = room_id!("!r:e.uk");
1154 let session_meta = SessionMeta { user_id: user_id.to_owned(), device_id: "FOOBAR".into() };
1155 let state_store;
1156
1157 {
1158 let client = {
1159 let store = StoreConfig::new("cross-process-foo".to_owned());
1160 state_store = store.state_store.clone();
1161
1162 let client = BaseClient::new(store, ThreadingSupport::Disabled);
1163 client
1164 .activate(
1165 session_meta.clone(),
1166 RoomLoadSettings::default(),
1167 #[cfg(feature = "e2e-encryption")]
1168 None,
1169 )
1170 .await
1171 .expect("`activate` failed!");
1172
1173 client
1174 };
1175
1176 let room = room_with_name("Hello World", user_id);
1179 let response = response_with_room(room_id, room);
1180 client
1181 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1182 .await
1183 .expect("Failed to process sync");
1184
1185 let room = client.get_room(room_id).expect("No room found");
1186 assert_eq!(room.cached_display_name().unwrap().to_string(), "Hello World");
1187 }
1188
1189 {
1190 let client = {
1191 let mut store = StoreConfig::new("cross-process-foo".to_owned());
1192 store.state_store = state_store;
1193 let client = BaseClient::new(store, ThreadingSupport::Disabled);
1194 client
1195 .activate(
1196 session_meta,
1197 RoomLoadSettings::default(),
1198 #[cfg(feature = "e2e-encryption")]
1199 None,
1200 )
1201 .await
1202 .expect("`activate` failed!");
1203
1204 client
1205 };
1206
1207 let room = client.get_room(room_id).expect("No room found");
1208 assert_eq!(room.cached_display_name().unwrap().to_string(), "Hello World");
1209 }
1210 }
1211
1212 #[async_test]
1213 async fn test_compute_heroes_from_sliding_sync() {
1214 let client = logged_in_base_client(None).await;
1216 let room_id = room_id!("!r:e.uk");
1217 let gordon = user_id!("@gordon:e.uk").to_owned();
1218 let alice = user_id!("@alice:e.uk").to_owned();
1219
1220 let mut room = http::response::Room::new();
1223 room.heroes = Some(vec![
1224 assign!(http::response::Hero::new(gordon), {
1225 name: Some("Gordon".to_owned()),
1226 }),
1227 assign!(http::response::Hero::new(alice), {
1228 name: Some("Alice".to_owned()),
1229 avatar: Some(owned_mxc_uri!("mxc://e.uk/med1"))
1230 }),
1231 ]);
1232 let response = response_with_room(room_id, room);
1233 let _sync_resp = client
1234 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1235 .await
1236 .expect("Failed to process sync");
1237
1238 let client_room = client.get_room(room_id).expect("No room found");
1240 assert_eq!(client_room.room_id(), room_id);
1241 assert_eq!(client_room.state(), RoomState::Joined);
1242
1243 assert_eq!(
1245 client_room.clone_info().summary.heroes(),
1246 &[
1247 RoomHero {
1248 user_id: owned_user_id!("@gordon:e.uk"),
1249 display_name: Some("Gordon".to_owned()),
1250 avatar_url: None
1251 },
1252 RoomHero {
1253 user_id: owned_user_id!("@alice:e.uk"),
1254 display_name: Some("Alice".to_owned()),
1255 avatar_url: Some(owned_mxc_uri!("mxc://e.uk/med1"))
1256 },
1257 ]
1258 );
1259 }
1260
1261 #[async_test]
1262 async fn test_last_event_from_sliding_sync_is_cached() {
1263 let client = logged_in_base_client(None).await;
1265 let room_id = room_id!("!r:e.uk");
1266 let event_a = json!({
1267 "sender":"@alice:example.com",
1268 "type":"m.room.message",
1269 "event_id": "$ida",
1270 "origin_server_ts": 12344446,
1271 "content":{"body":"A", "msgtype": "m.text"}
1272 });
1273 let event_b = json!({
1274 "sender":"@alice:example.com",
1275 "type":"m.room.message",
1276 "event_id": "$idb",
1277 "origin_server_ts": 12344447,
1278 "content":{"body":"B", "msgtype": "m.text"}
1279 });
1280
1281 let events = &[event_a, event_b.clone()];
1283 let room = room_with_timeline(events);
1284 let response = response_with_room(room_id, room);
1285 client
1286 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1287 .await
1288 .expect("Failed to process sync");
1289
1290 let client_room = client.get_room(room_id).expect("No room found");
1292 assert_eq!(
1293 ev_id(client_room.latest_event().map(|latest_event| latest_event.event().clone())),
1294 "$idb"
1295 );
1296 }
1297
1298 #[async_test]
1299 async fn test_last_knock_event_from_sliding_sync_is_cached_if_user_has_permissions() {
1300 let own_user_id = user_id!("@me:e.uk");
1301 let client = logged_in_base_client(Some(own_user_id)).await;
1303 let room_id = room_id!("!r:e.uk");
1304
1305 let power_levels = json!({
1307 "sender":"@alice:example.com",
1308 "state_key":"",
1309 "type":"m.room.power_levels",
1310 "event_id": "$idb",
1311 "origin_server_ts": 12344445,
1312 "content":{ "invite": 100, "kick": 100, "users": { own_user_id: 100 } },
1313 "room_id": room_id,
1314 });
1315
1316 let knock_event = json!({
1318 "sender":"@alice:example.com",
1319 "state_key":"@alice:example.com",
1320 "type":"m.room.member",
1321 "event_id": "$ida",
1322 "origin_server_ts": 12344446,
1323 "content":{"membership": "knock"},
1324 "room_id": room_id,
1325 });
1326
1327 let events = &[knock_event];
1329 let mut room = room_with_timeline(events);
1330 room.required_state.push(Raw::new(&power_levels).unwrap().cast());
1331 let response = response_with_room(room_id, room);
1332 client
1333 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1334 .await
1335 .expect("Failed to process sync");
1336
1337 let client_room = client.get_room(room_id).expect("No room found");
1339 assert_eq!(
1340 ev_id(client_room.latest_event().map(|latest_event| latest_event.event().clone())),
1341 "$ida"
1342 );
1343 }
1344
1345 #[async_test]
1346 async fn test_last_knock_event_from_sliding_sync_is_not_cached_without_permissions() {
1347 let own_user_id = user_id!("@me:e.uk");
1348 let client = logged_in_base_client(Some(own_user_id)).await;
1350 let room_id = room_id!("!r:e.uk");
1351
1352 let power_levels = json!({
1355 "sender":"@alice:example.com",
1356 "state_key":"",
1357 "type":"m.room.power_levels",
1358 "event_id": "$idb",
1359 "origin_server_ts": 12344445,
1360 "content":{ "invite": 50, "kick": 50, "users": { own_user_id: 0 } },
1361 "room_id": room_id,
1362 });
1363
1364 let knock_event = json!({
1366 "sender":"@alice:example.com",
1367 "state_key":"@alice:example.com",
1368 "type":"m.room.member",
1369 "event_id": "$ida",
1370 "origin_server_ts": 12344446,
1371 "content":{"membership": "knock"},
1372 "room_id": room_id,
1373 });
1374
1375 let events = &[knock_event];
1377 let mut room = room_with_timeline(events);
1378 room.required_state.push(Raw::new(&power_levels).unwrap().cast());
1379 let response = response_with_room(room_id, room);
1380 client
1381 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1382 .await
1383 .expect("Failed to process sync");
1384
1385 let client_room = client.get_room(room_id).expect("No room found");
1387 assert!(client_room.latest_event().is_none());
1388 }
1389
1390 #[async_test]
1391 async fn test_last_non_knock_member_state_event_from_sliding_sync_is_not_cached() {
1392 let client = logged_in_base_client(None).await;
1394 let room_id = room_id!("!r:e.uk");
1395 let join_event = json!({
1397 "sender":"@alice:example.com",
1398 "state_key":"@alice:example.com",
1399 "type":"m.room.member",
1400 "event_id": "$ida",
1401 "origin_server_ts": 12344446,
1402 "content":{"membership": "join"},
1403 "room_id": room_id,
1404 });
1405
1406 let events = &[join_event];
1408 let room = room_with_timeline(events);
1409 let response = response_with_room(room_id, room);
1410 client
1411 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1412 .await
1413 .expect("Failed to process sync");
1414
1415 let client_room = client.get_room(room_id).expect("No room found");
1417 assert!(client_room.latest_event().is_none());
1418 }
1419
1420 #[async_test]
1421 async fn test_cached_latest_event_can_be_redacted() {
1422 let client = logged_in_base_client(None).await;
1424 let room_id = room_id!("!r:e.uk");
1425 let event_a = json!({
1426 "sender": "@alice:example.com",
1427 "type": "m.room.message",
1428 "event_id": "$ida",
1429 "origin_server_ts": 12344446,
1430 "content": { "body":"A", "msgtype": "m.text" },
1431 });
1432
1433 let room = room_with_timeline(&[event_a]);
1435 let response = response_with_room(room_id, room);
1436 client
1437 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1438 .await
1439 .expect("Failed to process sync");
1440
1441 let client_room = client.get_room(room_id).expect("No room found");
1443 assert_eq!(
1444 ev_id(client_room.latest_event().map(|latest_event| latest_event.event().clone())),
1445 "$ida"
1446 );
1447
1448 let redaction = json!({
1449 "sender": "@alice:example.com",
1450 "type": "m.room.redaction",
1451 "event_id": "$idb",
1452 "redacts": "$ida",
1453 "origin_server_ts": 12344448,
1454 "content": {},
1455 });
1456
1457 let room = room_with_timeline(&[redaction]);
1459 let response = response_with_room(room_id, room);
1460 client
1461 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1462 .await
1463 .expect("Failed to process sync");
1464
1465 let client_room = client.get_room(room_id).expect("No room found");
1467 let latest_event = client_room.latest_event().unwrap();
1468 assert_eq!(latest_event.event_id().unwrap(), "$ida");
1469
1470 assert_matches!(
1472 latest_event.event().raw().deserialize().unwrap(),
1473 AnySyncTimelineEvent::MessageLike(AnySyncMessageLikeEvent::RoomMessage(
1474 SyncRoomMessageEvent::Redacted(_)
1475 ))
1476 );
1477 }
1478
1479 #[cfg(feature = "e2e-encryption")]
1480 #[async_test]
1481 async fn test_when_no_events_we_dont_cache_any() {
1482 let events = &[];
1483 let chosen = choose_event_to_cache(events).await;
1484 assert!(chosen.is_none());
1485 }
1486
1487 #[cfg(feature = "e2e-encryption")]
1488 #[async_test]
1489 async fn test_when_only_one_event_we_cache_it() {
1490 let event1 = make_event("m.room.message", "$1");
1491 let events = std::slice::from_ref(&event1);
1492 let chosen = choose_event_to_cache(events).await;
1493 assert_eq!(ev_id(chosen), rawev_id(event1));
1494 }
1495
1496 #[cfg(feature = "e2e-encryption")]
1497 #[async_test]
1498 async fn test_with_multiple_events_we_cache_the_last_one() {
1499 let event1 = make_event("m.room.message", "$1");
1500 let event2 = make_event("m.room.message", "$2");
1501 let events = &[event1, event2.clone()];
1502 let chosen = choose_event_to_cache(events).await;
1503 assert_eq!(ev_id(chosen), rawev_id(event2));
1504 }
1505
1506 #[cfg(feature = "e2e-encryption")]
1507 #[async_test]
1508 async fn test_cache_the_latest_relevant_event_and_ignore_irrelevant_ones_even_if_later() {
1509 let event1 = make_event("m.room.message", "$1");
1510 let event2 = make_event("m.room.message", "$2");
1511 let event3 = make_event("m.room.powerlevels", "$3");
1512 let event4 = make_event("m.room.powerlevels", "$5");
1513 let events = &[event1, event2.clone(), event3, event4];
1514 let chosen = choose_event_to_cache(events).await;
1515 assert_eq!(ev_id(chosen), rawev_id(event2));
1516 }
1517
1518 #[cfg(feature = "e2e-encryption")]
1519 #[async_test]
1520 async fn test_prefer_to_cache_nothing_rather_than_irrelevant_events() {
1521 let event1 = make_event("m.room.power_levels", "$1");
1522 let events = &[event1];
1523 let chosen = choose_event_to_cache(events).await;
1524 assert!(chosen.is_none());
1525 }
1526
1527 #[cfg(feature = "e2e-encryption")]
1528 #[async_test]
1529 async fn test_cache_encrypted_events_that_are_after_latest_message() {
1530 let event1 = make_event("m.room.message", "$1");
1532 let event2 = make_event("m.room.message", "$2");
1533 let event3 = make_encrypted_event("$3");
1534 let event4 = make_encrypted_event("$4");
1535 let events = &[event1, event2.clone(), event3.clone(), event4.clone()];
1536
1537 let room = make_room();
1539 let mut room_info = room.clone_info();
1540 cache_latest_events(&room, &mut room_info, events, None, None).await;
1541
1542 assert_eq!(
1544 ev_id(room_info.latest_event.as_ref().map(|latest_event| latest_event.event().clone())),
1545 rawev_id(event2.clone())
1546 );
1547
1548 room.set_room_info(room_info, RoomInfoNotableUpdateReasons::empty());
1549 assert_eq!(
1550 ev_id(room.latest_event().map(|latest_event| latest_event.event().clone())),
1551 rawev_id(event2)
1552 );
1553
1554 assert_eq!(rawevs_ids(&room.latest_encrypted_events), evs_ids(&[event3, event4]));
1556 }
1557
1558 #[cfg(feature = "e2e-encryption")]
1559 #[async_test]
1560 async fn test_dont_cache_encrypted_events_that_are_before_latest_message() {
1561 let event1 = make_encrypted_event("$1");
1563 let event2 = make_event("m.room.message", "$2");
1564 let event3 = make_encrypted_event("$3");
1565 let events = &[event1, event2.clone(), event3.clone()];
1566
1567 let room = make_room();
1569 let mut room_info = room.clone_info();
1570 cache_latest_events(&room, &mut room_info, events, None, None).await;
1571 room.set_room_info(room_info, RoomInfoNotableUpdateReasons::empty());
1572
1573 assert_eq!(
1575 ev_id(room.latest_event().map(|latest_event| latest_event.event().clone())),
1576 rawev_id(event2)
1577 );
1578
1579 assert_eq!(rawevs_ids(&room.latest_encrypted_events), evs_ids(&[event3]));
1581 }
1582
1583 #[cfg(feature = "e2e-encryption")]
1584 #[async_test]
1585 async fn test_skip_irrelevant_events_eg_receipts_even_if_after_message() {
1586 let event1 = make_event("m.room.message", "$1");
1589 let event2 = make_event("m.room.message", "$2");
1590 let event3 = make_encrypted_event("$3");
1591 let event4 = make_event("m.read", "$4");
1592 let event5 = make_encrypted_event("$5");
1593 let events = &[event1, event2.clone(), event3.clone(), event4, event5.clone()];
1594
1595 let room = make_room();
1597 let mut room_info = room.clone_info();
1598 cache_latest_events(&room, &mut room_info, events, None, None).await;
1599 room.set_room_info(room_info, RoomInfoNotableUpdateReasons::empty());
1600
1601 assert_eq!(
1603 ev_id(room.latest_event().map(|latest_event| latest_event.event().clone())),
1604 rawev_id(event2)
1605 );
1606
1607 assert_eq!(rawevs_ids(&room.latest_encrypted_events), evs_ids(&[event3, event5]));
1609 }
1610
1611 #[cfg(feature = "e2e-encryption")]
1612 #[async_test]
1613 async fn test_only_store_the_max_number_of_encrypted_events() {
1614 let evente = make_event("m.room.message", "$e");
1617 let eventd = make_event("m.room.message", "$d");
1618 let eventc = make_encrypted_event("$c");
1619 let event9 = make_encrypted_event("$9");
1620 let event8 = make_encrypted_event("$8");
1621 let event7 = make_encrypted_event("$7");
1622 let eventb = make_event("m.read", "$b");
1623 let event6 = make_encrypted_event("$6");
1624 let event5 = make_encrypted_event("$5");
1625 let event4 = make_encrypted_event("$4");
1626 let event3 = make_encrypted_event("$3");
1627 let event2 = make_encrypted_event("$2");
1628 let eventa = make_event("m.read", "$a");
1629 let event1 = make_encrypted_event("$1");
1630 let event0 = make_encrypted_event("$0");
1631 let events = &[
1632 evente,
1633 eventd.clone(),
1634 eventc,
1635 event9.clone(),
1636 event8.clone(),
1637 event7.clone(),
1638 eventb,
1639 event6.clone(),
1640 event5.clone(),
1641 event4.clone(),
1642 event3.clone(),
1643 event2.clone(),
1644 eventa,
1645 event1.clone(),
1646 event0.clone(),
1647 ];
1648
1649 let room = make_room();
1651 let mut room_info = room.clone_info();
1652 cache_latest_events(&room, &mut room_info, events, None, None).await;
1653 room.set_room_info(room_info, RoomInfoNotableUpdateReasons::empty());
1654
1655 assert_eq!(
1657 ev_id(room.latest_event().map(|latest_event| latest_event.event().clone())),
1658 rawev_id(eventd)
1659 );
1660
1661 assert_eq!(
1663 rawevs_ids(&room.latest_encrypted_events),
1664 evs_ids(&[
1665 event9, event8, event7, event6, event5, event4, event3, event2, event1, event0
1666 ])
1667 );
1668 }
1669
1670 #[cfg(feature = "e2e-encryption")]
1671 #[async_test]
1672 async fn test_dont_overflow_capacity_if_previous_encrypted_events_exist() {
1673 let room = make_room();
1675 let mut room_info = room.clone_info();
1676 cache_latest_events(
1677 &room,
1678 &mut room_info,
1679 &[
1680 make_encrypted_event("$0"),
1681 make_encrypted_event("$1"),
1682 make_encrypted_event("$2"),
1683 make_encrypted_event("$3"),
1684 make_encrypted_event("$4"),
1685 make_encrypted_event("$5"),
1686 make_encrypted_event("$6"),
1687 make_encrypted_event("$7"),
1688 make_encrypted_event("$8"),
1689 make_encrypted_event("$9"),
1690 ],
1691 None,
1692 None,
1693 )
1694 .await;
1695 room.set_room_info(room_info, RoomInfoNotableUpdateReasons::empty());
1696
1697 assert_eq!(room.latest_encrypted_events.read().unwrap().len(), 10);
1699
1700 let eventa = make_encrypted_event("$a");
1702 let mut room_info = room.clone_info();
1703 cache_latest_events(&room, &mut room_info, &[eventa], None, None).await;
1704 room.set_room_info(room_info, RoomInfoNotableUpdateReasons::empty());
1705
1706 assert!(!rawevs_ids(&room.latest_encrypted_events).contains(&"$0".to_owned()));
1708
1709 assert_eq!(rawevs_ids(&room.latest_encrypted_events)[9], "$a");
1711 }
1712
1713 #[cfg(feature = "e2e-encryption")]
1714 #[async_test]
1715 async fn test_existing_encrypted_events_are_deleted_if_we_receive_unencrypted() {
1716 let room = make_room();
1718 let mut room_info = room.clone_info();
1719 cache_latest_events(
1720 &room,
1721 &mut room_info,
1722 &[make_encrypted_event("$0"), make_encrypted_event("$1"), make_encrypted_event("$2")],
1723 None,
1724 None,
1725 )
1726 .await;
1727 room.set_room_info(room_info.clone(), RoomInfoNotableUpdateReasons::empty());
1728
1729 let eventa = make_event("m.room.message", "$a");
1731 let eventb = make_encrypted_event("$b");
1732 cache_latest_events(&room, &mut room_info, &[eventa, eventb], None, None).await;
1733 room.set_room_info(room_info, RoomInfoNotableUpdateReasons::empty());
1734
1735 assert_eq!(rawevs_ids(&room.latest_encrypted_events), &["$b"]);
1737
1738 assert_eq!(rawev_id(room.latest_event().unwrap().event().clone()), "$a");
1740 }
1741
1742 #[async_test]
1743 async fn test_recency_stamp_is_found_when_processing_sliding_sync_response() {
1744 let client = logged_in_base_client(None).await;
1746 let room_id = room_id!("!r:e.uk");
1747
1748 let room = assign!(http::response::Room::new(), {
1750 bump_stamp: Some(42u32.into()),
1751 });
1752 let response = response_with_room(room_id, room);
1753 client
1754 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1755 .await
1756 .expect("Failed to process sync");
1757
1758 let client_room = client.get_room(room_id).expect("No room found");
1760 assert_eq!(client_room.recency_stamp().expect("No recency stamp"), 42);
1761 }
1762
1763 #[async_test]
1764 async fn test_recency_stamp_can_be_overwritten_when_present_in_a_sliding_sync_response() {
1765 let client = logged_in_base_client(None).await;
1767 let room_id = room_id!("!r:e.uk");
1768
1769 {
1770 let room = assign!(http::response::Room::new(), {
1772 bump_stamp: Some(42u32.into()),
1773 });
1774 let response = response_with_room(room_id, room);
1775 client
1776 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1777 .await
1778 .expect("Failed to process sync");
1779
1780 let client_room = client.get_room(room_id).expect("No room found");
1782 assert_eq!(client_room.recency_stamp().expect("No recency stamp"), 42);
1783 }
1784
1785 {
1786 let room = assign!(http::response::Room::new(), {
1788 bump_stamp: None,
1789 });
1790 let response = response_with_room(room_id, room);
1791 client
1792 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1793 .await
1794 .expect("Failed to process sync");
1795
1796 let client_room = client.get_room(room_id).expect("No room found");
1798 assert_eq!(client_room.recency_stamp().expect("No recency stamp"), 42);
1799 }
1800
1801 {
1802 let room = assign!(http::response::Room::new(), {
1805 bump_stamp: Some(153u32.into()),
1806 });
1807 let response = response_with_room(room_id, room);
1808 client
1809 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1810 .await
1811 .expect("Failed to process sync");
1812
1813 let client_room = client.get_room(room_id).expect("No room found");
1815 assert_eq!(client_room.recency_stamp().expect("No recency stamp"), 153);
1816 }
1817 }
1818
1819 #[async_test]
1820 async fn test_recency_stamp_can_trigger_a_notable_update_reason() {
1821 let client = logged_in_base_client(None).await;
1823 let mut room_info_notable_update_stream = client.room_info_notable_update_receiver();
1824 let room_id = room_id!("!r:e.uk");
1825
1826 let room = assign!(http::response::Room::new(), {
1828 bump_stamp: Some(42u32.into()),
1829 });
1830 let response = response_with_room(room_id, room);
1831 client
1832 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1833 .await
1834 .expect("Failed to process sync");
1835
1836 assert_matches!(
1839 room_info_notable_update_stream.recv().await,
1840 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1841 assert_eq!(received_room_id, room_id);
1842 assert!(!received_reasons.contains(RoomInfoNotableUpdateReasons::RECENCY_STAMP));
1843 }
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::DISPLAY_NAME));
1850 }
1851 );
1852 assert!(room_info_notable_update_stream.is_empty());
1853
1854 let room = assign!(http::response::Room::new(), {
1856 bump_stamp: Some(43u32.into()),
1857 });
1858 let response = response_with_room(room_id, room);
1859 client
1860 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1861 .await
1862 .expect("Failed to process sync");
1863
1864 assert_matches!(
1866 room_info_notable_update_stream.recv().await,
1867 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1868 assert_eq!(received_room_id, room_id);
1869 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::RECENCY_STAMP));
1870 }
1871 );
1872 assert!(room_info_notable_update_stream.is_empty());
1873 }
1874
1875 #[async_test]
1876 async fn test_leaving_room_can_trigger_a_notable_update_reason() {
1877 let client = logged_in_base_client(None).await;
1879 let mut room_info_notable_update_stream = client.room_info_notable_update_receiver();
1880
1881 let room_id = room_id!("!r:e.uk");
1883 let room = http::response::Room::new();
1884 let response = response_with_room(room_id, room);
1885 client
1886 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1887 .await
1888 .expect("Failed to process sync");
1889
1890 assert_matches!(
1892 room_info_notable_update_stream.recv().await,
1893 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1894 assert_eq!(received_room_id, room_id);
1895 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::NONE));
1896 }
1897 );
1898 assert_matches!(
1899 room_info_notable_update_stream.recv().await,
1900 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1901 assert_eq!(received_room_id, room_id);
1902 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::DISPLAY_NAME));
1903 }
1904 );
1905
1906 let room_id = room_id!("!r:e.uk");
1908 let events = vec![Raw::from_json_string(
1909 json!({
1910 "type": "m.room.member",
1911 "event_id": "$3",
1912 "content": { "membership": "join" },
1913 "sender": "@u:h.uk",
1914 "origin_server_ts": 12344445,
1915 "state_key": "@u:e.uk",
1916 })
1917 .to_string(),
1918 )
1919 .unwrap()];
1920 let room = assign!(http::response::Room::new(), {
1921 required_state: events,
1922 });
1923 let response = response_with_room(room_id, room);
1924 client
1925 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1926 .await
1927 .expect("Failed to process sync");
1928
1929 assert_matches!(
1931 room_info_notable_update_stream.recv().await,
1932 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1933 assert_eq!(received_room_id, room_id);
1934 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::NONE));
1935 }
1936 );
1937 assert!(room_info_notable_update_stream.is_empty());
1938
1939 let events = vec![Raw::from_json_string(
1940 json!({
1941 "type": "m.room.member",
1942 "event_id": "$3",
1943 "content": { "membership": "leave" },
1944 "sender": "@u:h.uk",
1945 "origin_server_ts": 12344445,
1946 "state_key": "@u:e.uk",
1947 })
1948 .to_string(),
1949 )
1950 .unwrap()];
1951 let room = assign!(http::response::Room::new(), {
1952 required_state: events,
1953 });
1954 let response = response_with_room(room_id, room);
1955 client
1956 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1957 .await
1958 .expect("Failed to process sync");
1959
1960 assert_matches!(
1962 room_info_notable_update_stream.recv().await,
1963 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1964 assert_eq!(received_room_id, room_id);
1965 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::MEMBERSHIP));
1966 }
1967 );
1968 assert!(room_info_notable_update_stream.is_empty());
1969 }
1970
1971 #[async_test]
1972 async fn test_unread_marker_can_trigger_a_notable_update_reason() {
1973 let client = logged_in_base_client(None).await;
1975 let mut room_info_notable_update_stream = client.room_info_notable_update_receiver();
1976
1977 let room_id = room_id!("!r:e.uk");
1979 let room = http::response::Room::new();
1980 let response = response_with_room(room_id, room);
1981 client
1982 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1983 .await
1984 .expect("Failed to process sync");
1985
1986 assert_matches!(
1988 room_info_notable_update_stream.recv().await,
1989 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1990 assert_eq!(received_room_id, room_id);
1991 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::NONE), "{received_reasons:?}");
1992 }
1993 );
1994 assert_matches!(
1995 room_info_notable_update_stream.recv().await,
1996 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1997 assert_eq!(received_room_id, room_id);
1998 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::DISPLAY_NAME), "{received_reasons:?}");
1999 }
2000 );
2001 assert!(room_info_notable_update_stream.is_empty());
2002
2003 let room_id = room_id!("!r:e.uk");
2006 let room_account_data_events = vec![Raw::from_json_string(
2007 json!({
2008 "type": "m.marked_unread",
2009 "event_id": "$1",
2010 "content": { "unread": true },
2011 "sender": client.session_meta().unwrap().user_id,
2012 "origin_server_ts": 12344445,
2013 })
2014 .to_string(),
2015 )
2016 .unwrap()];
2017 let mut response = response_with_room(room_id, http::response::Room::new());
2018 response.extensions.account_data.rooms.insert(room_id.to_owned(), room_account_data_events);
2019
2020 client
2021 .process_sliding_sync(&response, &RequestedRequiredStates::default())
2022 .await
2023 .expect("Failed to process sync");
2024
2025 assert_matches!(
2027 room_info_notable_update_stream.recv().await,
2028 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
2029 assert_eq!(received_room_id, room_id);
2030 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::UNREAD_MARKER), "{received_reasons:?}");
2031 }
2032 );
2033
2034 client
2036 .process_sliding_sync(&response, &RequestedRequiredStates::default())
2037 .await
2038 .expect("Failed to process sync");
2039
2040 assert_matches!(
2041 room_info_notable_update_stream.recv().await,
2042 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
2043 assert_eq!(received_room_id, room_id);
2044 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::NONE), "{received_reasons:?}");
2045 }
2046 );
2047 assert!(room_info_notable_update_stream.is_empty());
2048
2049 let room_account_data_events = vec![Raw::from_json_string(
2051 json!({
2052 "type": "m.marked_unread",
2053 "event_id": "$1",
2054 "content": { "unread": false },
2055 "sender": client.session_meta().unwrap().user_id,
2056 "origin_server_ts": 12344445,
2057 })
2058 .to_string(),
2059 )
2060 .unwrap()];
2061 response.extensions.account_data.rooms.insert(room_id.to_owned(), room_account_data_events);
2062 client
2063 .process_sliding_sync(&response, &RequestedRequiredStates::default())
2064 .await
2065 .expect("Failed to process sync");
2066
2067 assert_matches!(
2068 room_info_notable_update_stream.recv().await,
2069 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
2070 assert_eq!(received_room_id, room_id);
2071 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::UNREAD_MARKER));
2072 }
2073 );
2074 assert!(room_info_notable_update_stream.is_empty());
2075 }
2076
2077 #[async_test]
2078 async fn test_unstable_unread_marker_is_ignored_after_stable() {
2079 let client = logged_in_base_client(None).await;
2081 let mut room_info_notable_update_stream = client.room_info_notable_update_receiver();
2082
2083 let room_id = room_id!("!r:e.uk");
2085 let room = http::response::Room::new();
2086 let response = response_with_room(room_id, room);
2087 client
2088 .process_sliding_sync(&response, &RequestedRequiredStates::default())
2089 .await
2090 .expect("Failed to process sync");
2091
2092 assert_matches!(
2094 room_info_notable_update_stream.recv().await,
2095 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
2096 assert_eq!(received_room_id, room_id);
2097 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::NONE), "{received_reasons:?}");
2098 }
2099 );
2100 assert_matches!(
2101 room_info_notable_update_stream.recv().await,
2102 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
2103 assert_eq!(received_room_id, room_id);
2104 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::DISPLAY_NAME), "{received_reasons:?}");
2105 }
2106 );
2107 assert!(room_info_notable_update_stream.is_empty());
2108
2109 let room_id = room_id!("!r:e.uk");
2112 let unstable_room_account_data_events = vec![Raw::from_json_string(
2113 json!({
2114 "type": "com.famedly.marked_unread",
2115 "event_id": "$1",
2116 "content": { "unread": true },
2117 "sender": client.session_meta().unwrap().user_id,
2118 "origin_server_ts": 12344445,
2119 })
2120 .to_string(),
2121 )
2122 .unwrap()];
2123 let mut response = response_with_room(room_id, http::response::Room::new());
2124 response
2125 .extensions
2126 .account_data
2127 .rooms
2128 .insert(room_id.to_owned(), unstable_room_account_data_events.clone());
2129
2130 client
2131 .process_sliding_sync(&response, &RequestedRequiredStates::default())
2132 .await
2133 .expect("Failed to process sync");
2134
2135 assert_matches!(
2137 room_info_notable_update_stream.recv().await,
2138 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
2139 assert_eq!(received_room_id, room_id);
2140 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::UNREAD_MARKER), "{received_reasons:?}");
2141 }
2142 );
2143 assert!(room_info_notable_update_stream.is_empty());
2144
2145 let stable_room_account_data_events = vec![Raw::from_json_string(
2147 json!({
2148 "type": "m.marked_unread",
2149 "event_id": "$1",
2150 "content": { "unread": false },
2151 "sender": client.session_meta().unwrap().user_id,
2152 "origin_server_ts": 12344445,
2153 })
2154 .to_string(),
2155 )
2156 .unwrap()];
2157 response
2158 .extensions
2159 .account_data
2160 .rooms
2161 .insert(room_id.to_owned(), stable_room_account_data_events);
2162 client
2163 .process_sliding_sync(&response, &RequestedRequiredStates::default())
2164 .await
2165 .expect("Failed to process sync");
2166
2167 assert_matches!(
2169 room_info_notable_update_stream.recv().await,
2170 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
2171 assert_eq!(received_room_id, room_id);
2172 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::UNREAD_MARKER));
2173 }
2174 );
2175 assert!(room_info_notable_update_stream.is_empty());
2176
2177 response
2180 .extensions
2181 .account_data
2182 .rooms
2183 .insert(room_id.to_owned(), unstable_room_account_data_events);
2184 client
2185 .process_sliding_sync(&response, &RequestedRequiredStates::default())
2186 .await
2187 .expect("Failed to process sync");
2188
2189 assert_matches!(
2191 room_info_notable_update_stream.recv().await,
2192 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
2193 assert_eq!(received_room_id, room_id);
2194 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::NONE), "{received_reasons:?}");
2195 }
2196 );
2197 assert!(room_info_notable_update_stream.is_empty());
2198
2199 let stable_room_account_data_events = vec![Raw::from_json_string(
2202 json!({
2203 "type": "m.marked_unread",
2204 "event_id": "$3",
2205 "content": { "unread": true },
2206 "sender": client.session_meta().unwrap().user_id,
2207 "origin_server_ts": 12344445,
2208 })
2209 .to_string(),
2210 )
2211 .unwrap()];
2212 response
2213 .extensions
2214 .account_data
2215 .rooms
2216 .insert(room_id.to_owned(), stable_room_account_data_events);
2217 client
2218 .process_sliding_sync(&response, &RequestedRequiredStates::default())
2219 .await
2220 .expect("Failed to process sync");
2221
2222 assert_matches!(
2224 room_info_notable_update_stream.recv().await,
2225 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
2226 assert_eq!(received_room_id, room_id);
2227 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::UNREAD_MARKER));
2228 }
2229 );
2230 assert!(room_info_notable_update_stream.is_empty());
2231 }
2232
2233 #[async_test]
2234 async fn test_pinned_events_are_updated_on_sync() {
2235 let user_a_id = user_id!("@a:e.uk");
2236 let client = logged_in_base_client(Some(user_a_id)).await;
2237 let room_id = room_id!("!r:e.uk");
2238 let pinned_event_id = owned_event_id!("$an-id:e.uk");
2239
2240 let mut room_response = http::response::Room::new();
2242 set_room_joined(&mut room_response, user_a_id);
2243 let response = response_with_room(room_id, room_response);
2244 client
2245 .process_sliding_sync(&response, &RequestedRequiredStates::default())
2246 .await
2247 .expect("Failed to process sync");
2248
2249 let room = client.get_room(room_id).unwrap();
2251 let pinned_event_ids = room.pinned_event_ids();
2252 assert_matches!(pinned_event_ids, None);
2253
2254 let mut room_response = http::response::Room::new();
2256 room_response.required_state.push(make_state_event(
2257 user_a_id,
2258 "",
2259 RoomPinnedEventsEventContent::new(vec![pinned_event_id.clone()]),
2260 None,
2261 ));
2262 let response = response_with_room(room_id, room_response);
2263 client
2264 .process_sliding_sync(&response, &RequestedRequiredStates::default())
2265 .await
2266 .expect("Failed to process sync");
2267
2268 let pinned_event_ids = room.pinned_event_ids().unwrap_or_default();
2269 assert_eq!(pinned_event_ids.len(), 1);
2270 assert_eq!(pinned_event_ids[0], pinned_event_id);
2271
2272 let mut room_response = http::response::Room::new();
2274 room_response.required_state.push(make_state_event(
2275 user_a_id,
2276 "",
2277 RoomPinnedEventsEventContent::new(Vec::new()),
2278 None,
2279 ));
2280 let response = response_with_room(room_id, room_response);
2281 client
2282 .process_sliding_sync(&response, &RequestedRequiredStates::default())
2283 .await
2284 .expect("Failed to process sync");
2285 let pinned_event_ids = room.pinned_event_ids().unwrap();
2286 assert!(pinned_event_ids.is_empty());
2287 }
2288
2289 #[async_test]
2290 async fn test_dms_are_processed_in_any_sync_response() {
2291 let current_user_id = user_id!("@current:e.uk");
2292 let client = logged_in_base_client(Some(current_user_id)).await;
2293 let user_a_id = user_id!("@a:e.uk");
2294 let user_b_id = user_id!("@b:e.uk");
2295 let room_id_1 = room_id!("!r:e.uk");
2296 let room_id_2 = room_id!("!s:e.uk");
2297
2298 let mut room_response = http::response::Room::new();
2299 set_room_joined(&mut room_response, user_a_id);
2300 let mut response = response_with_room(room_id_1, room_response);
2301 let mut direct_content: BTreeMap<OwnedDirectUserIdentifier, Vec<OwnedRoomId>> =
2302 BTreeMap::new();
2303 direct_content.insert(user_a_id.into(), vec![room_id_1.to_owned()]);
2304 direct_content.insert(user_b_id.into(), vec![room_id_2.to_owned()]);
2305 response
2306 .extensions
2307 .account_data
2308 .global
2309 .push(make_global_account_data_event(DirectEventContent(direct_content)));
2310 client
2311 .process_sliding_sync(&response, &RequestedRequiredStates::default())
2312 .await
2313 .expect("Failed to process sync");
2314
2315 let room_1 = client.get_room(room_id_1).unwrap();
2316 assert!(room_1.is_direct().await.unwrap());
2317
2318 let mut room_response = http::response::Room::new();
2320 set_room_joined(&mut room_response, user_b_id);
2321 let response = response_with_room(room_id_2, room_response);
2322 client
2323 .process_sliding_sync(&response, &RequestedRequiredStates::default())
2324 .await
2325 .expect("Failed to process sync");
2326
2327 let room_2 = client.get_room(room_id_2).unwrap();
2328 assert!(room_2.is_direct().await.unwrap());
2329 }
2330
2331 #[async_test]
2332 async fn test_room_encryption_state_is_and_is_not_encrypted() {
2333 let user_id = user_id!("@raclette:patate");
2334 let client = logged_in_base_client(Some(user_id)).await;
2335 let room_id_0 = room_id!("!r0");
2336 let room_id_1 = room_id!("!r1");
2337 let room_id_2 = room_id!("!r2");
2338
2339 let requested_required_states = RequestedRequiredStates::from(&{
2356 let mut request = http::Request::new();
2357
2358 request.room_subscriptions.insert(room_id_0.to_owned(), {
2359 let mut room_subscription = http::request::RoomSubscription::default();
2360
2361 room_subscription
2362 .required_state
2363 .push((StateEventType::RoomEncryption, "".to_owned()));
2364
2365 room_subscription
2366 });
2367
2368 request
2369 });
2370
2371 let mut response = http::Response::new("0".to_owned());
2372
2373 {
2377 let not_encrypted_room = http::response::Room::new();
2378 let mut encrypted_room = http::response::Room::new();
2379 set_room_is_encrypted(&mut encrypted_room, user_id);
2380
2381 response.rooms.insert(room_id_0.to_owned(), encrypted_room.clone());
2382 response.rooms.insert(room_id_1.to_owned(), encrypted_room);
2383 response.rooms.insert(room_id_2.to_owned(), not_encrypted_room);
2384 }
2385
2386 client
2387 .process_sliding_sync(&response, &requested_required_states)
2388 .await
2389 .expect("Failed to process sync");
2390
2391 assert_matches!(
2393 client.get_room(room_id_0).unwrap().encryption_state(),
2394 EncryptionState::Encrypted
2395 );
2396 assert_matches!(
2397 client.get_room(room_id_1).unwrap().encryption_state(),
2398 EncryptionState::Encrypted
2399 );
2400 assert_matches!(
2402 client.get_room(room_id_2).unwrap().encryption_state(),
2403 EncryptionState::NotEncrypted
2404 )
2405 }
2406
2407 #[async_test]
2408 async fn test_room_encryption_state_is_unknown() {
2409 let user_id = user_id!("@raclette:patate");
2410 let client = logged_in_base_client(Some(user_id)).await;
2411 let room_id_0 = room_id!("!r0");
2412 let room_id_1 = room_id!("!r1");
2413
2414 let requested_required_states = RequestedRequiredStates::from(&http::Request::new());
2427
2428 let mut response = http::Response::new("0".to_owned());
2429
2430 {
2432 let not_encrypted_room = http::response::Room::new();
2433 let mut encrypted_room = http::response::Room::new();
2434 set_room_is_encrypted(&mut encrypted_room, user_id);
2435
2436 response.rooms.insert(room_id_0.to_owned(), encrypted_room);
2437 response.rooms.insert(room_id_1.to_owned(), not_encrypted_room);
2438 }
2439
2440 client
2441 .process_sliding_sync(&response, &requested_required_states)
2442 .await
2443 .expect("Failed to process sync");
2444
2445 assert_matches!(
2448 client.get_room(room_id_0).unwrap().encryption_state(),
2449 EncryptionState::Encrypted
2450 );
2451 assert_matches!(
2454 client.get_room(room_id_1).unwrap().encryption_state(),
2455 EncryptionState::Unknown
2456 );
2457 }
2458
2459 #[cfg(feature = "e2e-encryption")]
2460 async fn choose_event_to_cache(events: &[TimelineEvent]) -> Option<TimelineEvent> {
2461 let room = make_room();
2462 let mut room_info = room.clone_info();
2463 cache_latest_events(&room, &mut room_info, events, None, None).await;
2464 room.set_room_info(room_info, RoomInfoNotableUpdateReasons::empty());
2465 room.latest_event().map(|latest_event| latest_event.event().clone())
2466 }
2467
2468 #[cfg(feature = "e2e-encryption")]
2469 fn rawev_id(event: TimelineEvent) -> String {
2470 event.event_id().unwrap().to_string()
2471 }
2472
2473 fn ev_id(event: Option<TimelineEvent>) -> String {
2474 event.unwrap().event_id().unwrap().to_string()
2475 }
2476
2477 #[cfg(feature = "e2e-encryption")]
2478 fn rawevs_ids(events: &Arc<SyncRwLock<RingBuffer<Raw<AnySyncTimelineEvent>>>>) -> Vec<String> {
2479 events.read().unwrap().iter().map(|e| e.get_field("event_id").unwrap().unwrap()).collect()
2480 }
2481
2482 #[cfg(feature = "e2e-encryption")]
2483 fn evs_ids(events: &[TimelineEvent]) -> Vec<String> {
2484 events.iter().map(|e| e.event_id().unwrap().to_string()).collect()
2485 }
2486
2487 #[cfg(feature = "e2e-encryption")]
2488 fn make_room() -> Room {
2489 let (sender, _receiver) = tokio::sync::broadcast::channel(1);
2490
2491 Room::new(
2492 user_id!("@u:e.co"),
2493 Arc::new(MemoryStore::new()),
2494 room_id!("!r:e.co"),
2495 RoomState::Joined,
2496 sender,
2497 )
2498 }
2499
2500 fn make_raw_event(event_type: &str, id: &str) -> Raw<AnySyncTimelineEvent> {
2501 Raw::from_json_string(
2502 json!({
2503 "type": event_type,
2504 "event_id": id,
2505 "content": { "msgtype": "m.text", "body": "my msg" },
2506 "sender": "@u:h.uk",
2507 "origin_server_ts": 12344445,
2508 })
2509 .to_string(),
2510 )
2511 .unwrap()
2512 }
2513
2514 #[cfg(feature = "e2e-encryption")]
2515 fn make_event(event_type: &str, id: &str) -> TimelineEvent {
2516 TimelineEvent::from_plaintext(make_raw_event(event_type, id))
2517 }
2518
2519 #[cfg(feature = "e2e-encryption")]
2520 fn make_encrypted_event(id: &str) -> TimelineEvent {
2521 TimelineEvent::from_utd(
2522 Raw::from_json_string(
2523 json!({
2524 "type": "m.room.encrypted",
2525 "event_id": id,
2526 "content": {
2527 "algorithm": "m.megolm.v1.aes-sha2",
2528 "ciphertext": "",
2529 "sender_key": "",
2530 "device_id": "",
2531 "session_id": "",
2532 },
2533 "sender": "@u:h.uk",
2534 "origin_server_ts": 12344445,
2535 })
2536 .to_string(),
2537 )
2538 .unwrap(),
2539 UnableToDecryptInfo {
2540 session_id: Some("".to_owned()),
2541 reason: UnableToDecryptReason::MissingMegolmSession { withheld_code: None },
2542 },
2543 )
2544 }
2545
2546 async fn membership(
2547 client: &BaseClient,
2548 room_id: &RoomId,
2549 user_id: &UserId,
2550 ) -> MembershipState {
2551 let room = client.get_room(room_id).expect("Room not found!");
2552 let member = room.get_member(user_id).await.unwrap().expect("B not in room");
2553 member.membership().clone()
2554 }
2555
2556 fn direct_targets(client: &BaseClient, room_id: &RoomId) -> HashSet<OwnedDirectUserIdentifier> {
2557 let room = client.get_room(room_id).expect("Room not found!");
2558 room.direct_targets()
2559 }
2560
2561 async fn create_dm(
2564 client: &BaseClient,
2565 room_id: &RoomId,
2566 my_id: &UserId,
2567 their_id: &UserId,
2568 other_state: MembershipState,
2569 ) {
2570 let mut room = http::response::Room::new();
2571 set_room_joined(&mut room, my_id);
2572
2573 match other_state {
2574 MembershipState::Join => {
2575 room.joined_count = Some(uint!(2));
2576 room.invited_count = None;
2577 }
2578
2579 MembershipState::Invite => {
2580 room.joined_count = Some(uint!(1));
2581 room.invited_count = Some(uint!(1));
2582 }
2583
2584 _ => {
2585 room.joined_count = Some(uint!(1));
2586 room.invited_count = None;
2587 }
2588 }
2589
2590 room.required_state.push(make_membership_event(their_id, other_state));
2591
2592 let mut response = response_with_room(room_id, room);
2593 set_direct_with(&mut response, their_id.to_owned(), vec![room_id.to_owned()]);
2594 client
2595 .process_sliding_sync(&response, &RequestedRequiredStates::default())
2596 .await
2597 .expect("Failed to process sync");
2598 }
2599
2600 async fn update_room_membership(
2602 client: &BaseClient,
2603 room_id: &RoomId,
2604 user_id: &UserId,
2605 new_state: MembershipState,
2606 ) {
2607 let mut room = http::response::Room::new();
2608 room.required_state.push(make_membership_event(user_id, new_state));
2609 let response = response_with_room(room_id, room);
2610 client
2611 .process_sliding_sync(&response, &RequestedRequiredStates::default())
2612 .await
2613 .expect("Failed to process sync");
2614 }
2615
2616 fn set_direct_with(
2617 response: &mut http::Response,
2618 user_id: OwnedUserId,
2619 room_ids: Vec<OwnedRoomId>,
2620 ) {
2621 let mut direct_content: BTreeMap<OwnedDirectUserIdentifier, Vec<OwnedRoomId>> =
2622 BTreeMap::new();
2623 direct_content.insert(user_id.into(), room_ids);
2624 response
2625 .extensions
2626 .account_data
2627 .global
2628 .push(make_global_account_data_event(DirectEventContent(direct_content)));
2629 }
2630
2631 fn response_with_room(room_id: &RoomId, room: http::response::Room) -> http::Response {
2632 let mut response = http::Response::new("5".to_owned());
2633 response.rooms.insert(room_id.to_owned(), room);
2634 response
2635 }
2636
2637 fn room_with_avatar(avatar_uri: &MxcUri, user_id: &UserId) -> http::response::Room {
2638 let mut room = http::response::Room::new();
2639
2640 let mut avatar_event_content = RoomAvatarEventContent::new();
2641 avatar_event_content.url = Some(avatar_uri.to_owned());
2642
2643 room.required_state.push(make_state_event(user_id, "", avatar_event_content, None));
2644
2645 room
2646 }
2647
2648 fn room_with_canonical_alias(
2649 room_alias_id: &RoomAliasId,
2650 user_id: &UserId,
2651 ) -> http::response::Room {
2652 let mut room = http::response::Room::new();
2653
2654 let mut canonical_alias_event_content = RoomCanonicalAliasEventContent::new();
2655 canonical_alias_event_content.alias = Some(room_alias_id.to_owned());
2656
2657 room.required_state.push(make_state_event(
2658 user_id,
2659 "",
2660 canonical_alias_event_content,
2661 None,
2662 ));
2663
2664 room
2665 }
2666
2667 fn room_with_name(name: &str, user_id: &UserId) -> http::response::Room {
2668 let mut room = http::response::Room::new();
2669
2670 let name_event_content = RoomNameEventContent::new(name.to_owned());
2671
2672 room.required_state.push(make_state_event(user_id, "", name_event_content, None));
2673
2674 room
2675 }
2676
2677 fn room_with_timeline(events: &[serde_json::Value]) -> http::response::Room {
2678 let mut room = http::response::Room::new();
2679 room.timeline.extend(
2680 events
2681 .iter()
2682 .map(|e| Raw::from_json_string(e.to_string()).unwrap())
2683 .collect::<Vec<_>>(),
2684 );
2685 room
2686 }
2687
2688 fn set_room_name(room: &mut http::response::Room, sender: &UserId, name: String) {
2689 room.required_state.push(make_state_event(
2690 sender,
2691 "",
2692 RoomNameEventContent::new(name),
2693 None,
2694 ));
2695 }
2696
2697 fn set_room_invited(room: &mut http::response::Room, inviter: &UserId, invitee: &UserId) {
2698 let evt = Raw::new(&json!({
2702 "type": "m.room.member",
2703 "sender": inviter,
2704 "content": {
2705 "is_direct": true,
2706 "membership": "invite",
2707 },
2708 "state_key": invitee,
2709 }))
2710 .expect("Failed to make raw event")
2711 .cast();
2712
2713 room.invite_state = Some(vec![evt]);
2714
2715 room.required_state.push(make_state_event(
2718 inviter,
2719 invitee.as_str(),
2720 RoomMemberEventContent::new(MembershipState::Invite),
2721 None,
2722 ));
2723 }
2724
2725 fn set_room_knocked(room: &mut http::response::Room, knocker: &UserId) {
2726 let evt = Raw::new(&json!({
2730 "type": "m.room.member",
2731 "sender": knocker,
2732 "content": {
2733 "is_direct": true,
2734 "membership": "knock",
2735 },
2736 "state_key": knocker,
2737 }))
2738 .expect("Failed to make raw event")
2739 .cast();
2740
2741 room.invite_state = Some(vec![evt]);
2742 }
2743
2744 fn set_room_joined(room: &mut http::response::Room, user_id: &UserId) {
2745 room.required_state.push(make_membership_event(user_id, MembershipState::Join));
2746 }
2747
2748 fn set_room_left(room: &mut http::response::Room, user_id: &UserId) {
2749 room.required_state.push(make_membership_event(user_id, MembershipState::Leave));
2750 }
2751
2752 fn set_room_left_as_timeline_event(room: &mut http::response::Room, user_id: &UserId) {
2753 room.timeline.push(make_membership_event(user_id, MembershipState::Leave));
2754 }
2755
2756 fn set_room_is_encrypted(room: &mut http::response::Room, user_id: &UserId) {
2757 room.required_state.push(make_encryption_event(user_id));
2758 }
2759
2760 fn make_membership_event<K>(user_id: &UserId, state: MembershipState) -> Raw<K> {
2761 make_state_event(user_id, user_id.as_str(), RoomMemberEventContent::new(state), None)
2762 }
2763
2764 fn make_encryption_event<K>(user_id: &UserId) -> Raw<K> {
2765 make_state_event(user_id, "", RoomEncryptionEventContent::with_recommended_defaults(), None)
2766 }
2767
2768 fn make_global_account_data_event<C: GlobalAccountDataEventContent, E>(content: C) -> Raw<E> {
2769 Raw::new(&json!({
2770 "type": content.event_type(),
2771 "content": content,
2772 }))
2773 .expect("Failed to create account data event")
2774 .cast()
2775 }
2776
2777 fn make_state_event<C: StateEventContent, E>(
2778 sender: &UserId,
2779 state_key: &str,
2780 content: C,
2781 prev_content: Option<C>,
2782 ) -> Raw<E> {
2783 let unsigned = if let Some(prev_content) = prev_content {
2784 json!({ "prev_content": prev_content })
2785 } else {
2786 json!({})
2787 };
2788
2789 Raw::new(&json!({
2790 "type": content.event_type(),
2791 "state_key": state_key,
2792 "content": content,
2793 "event_id": event_id!("$evt"),
2794 "sender": sender,
2795 "origin_server_ts": 10,
2796 "unsigned": unsigned,
2797 }))
2798 .expect("Failed to create state event")
2799 .cast()
2800 }
2801}