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