1use std::collections::BTreeMap;
18#[cfg(feature = "e2e-encryption")]
19use std::ops::Deref;
20
21#[cfg(feature = "e2e-encryption")]
22use matrix_sdk_common::deserialized_responses::TimelineEvent;
23#[cfg(feature = "e2e-encryption")]
24use ruma::events::AnyToDeviceEvent;
25use ruma::{
26 api::client::sync::sync_events::{
27 v3::{self, InvitedRoom, KnockedRoom},
28 v5 as http,
29 },
30 events::{
31 room::member::MembershipState, AnyRoomAccountDataEvent, AnyStrippedStateEvent,
32 AnySyncStateEvent, StateEventType,
33 },
34 serde::Raw,
35 JsOption, OwnedRoomId, RoomId, UserId,
36};
37use tracing::{instrument, trace, warn};
38
39use super::BaseClient;
40use crate::{
41 error::Result,
42 read_receipts::{compute_unread_counts, PreviousEventsProvider},
43 response_processors::AccountDataProcessor,
44 rooms::{
45 normal::{RoomHero, RoomInfoNotableUpdateReasons},
46 RoomState,
47 },
48 ruma::assign,
49 store::{ambiguity_map::AmbiguityCache, BaseStateStore, StateChanges},
50 sync::{JoinedRoomUpdate, LeftRoomUpdate, Notification, RoomUpdates, SyncResponse},
51 RequestedRequiredStates, Room, RoomInfo,
52};
53#[cfg(feature = "e2e-encryption")]
54use crate::{
55 latest_event::{is_suitable_for_latest_event, LatestEvent, PossibleLatestEvent},
56 RoomMemberships,
57};
58
59impl BaseClient {
60 #[cfg(feature = "e2e-encryption")]
61 pub async fn process_sliding_sync_e2ee(
69 &self,
70 to_device: Option<&http::response::ToDevice>,
71 e2ee: &http::response::E2EE,
72 ) -> Result<Option<Vec<Raw<AnyToDeviceEvent>>>> {
73 if to_device.is_none() && e2ee.is_empty() {
74 return Ok(None);
75 }
76
77 let to_device_events =
78 to_device.as_ref().map(|to_device| to_device.events.clone()).unwrap_or_default();
79
80 trace!(
81 to_device_events = to_device_events.len(),
82 device_one_time_keys_count = e2ee.device_one_time_keys_count.len(),
83 device_unused_fallback_key_types =
84 e2ee.device_unused_fallback_key_types.as_ref().map(|v| v.len()),
85 "Processing sliding sync e2ee events",
86 );
87
88 let mut changes = StateChanges::default();
89 let mut room_info_notable_updates =
90 BTreeMap::<OwnedRoomId, RoomInfoNotableUpdateReasons>::new();
91
92 let to_device = self
96 .preprocess_to_device_events(
97 matrix_sdk_crypto::EncryptionSyncChanges {
98 to_device_events,
99 changed_devices: &e2ee.device_lists,
100 one_time_keys_counts: &e2ee.device_one_time_keys_count,
101 unused_fallback_keys: e2ee.device_unused_fallback_key_types.as_deref(),
102 next_batch_token: to_device
103 .as_ref()
104 .map(|to_device| to_device.next_batch.clone()),
105 },
106 &mut changes,
107 &mut room_info_notable_updates,
108 )
109 .await?;
110
111 trace!("ready to submit e2ee changes to store");
112 let prev_ignored_user_list = self.load_previous_ignored_user_list().await;
113 self.state_store.save_changes(&changes).await?;
114 self.apply_changes(&changes, room_info_notable_updates, prev_ignored_user_list);
115 trace!("applied e2ee changes");
116
117 Ok(Some(to_device))
118 }
119
120 #[instrument(skip_all, level = "trace")]
129 pub async fn process_sliding_sync<PEP: PreviousEventsProvider>(
130 &self,
131 response: &http::Response,
132 previous_events_provider: &PEP,
133 requested_required_states: &RequestedRequiredStates,
134 ) -> Result<SyncResponse> {
135 let http::Response {
136 rooms,
140 lists,
141 extensions,
142 ..
145 } = response;
146
147 trace!(
148 rooms = rooms.len(),
149 lists = lists.len(),
150 has_extensions = !extensions.is_empty(),
151 "Processing sliding sync room events"
152 );
153
154 if rooms.is_empty() && extensions.is_empty() {
155 return Ok(SyncResponse::default());
158 };
159
160 let mut changes = StateChanges::default();
161 let mut room_info_notable_updates =
162 BTreeMap::<OwnedRoomId, RoomInfoNotableUpdateReasons>::new();
163
164 let store = self.state_store.clone();
165 let mut ambiguity_cache = AmbiguityCache::new(store.inner.clone());
166
167 let account_data_processor = AccountDataProcessor::process(&extensions.account_data.global);
168
169 let mut new_rooms = RoomUpdates::default();
170 let mut notifications = Default::default();
171 let mut rooms_account_data = extensions.account_data.rooms.clone();
172
173 let user_id = self
174 .session_meta()
175 .expect("Sliding sync shouldn't run without an authenticated user.")
176 .user_id
177 .to_owned();
178
179 for (room_id, response_room_data) in rooms {
180 let (room_info, joined_room, left_room, invited_room, knocked_room) = self
181 .process_sliding_sync_room(
182 room_id,
183 requested_required_states.for_room(room_id),
184 response_room_data,
185 &mut rooms_account_data,
186 &store,
187 &user_id,
188 &account_data_processor,
189 &mut changes,
190 &mut room_info_notable_updates,
191 &mut notifications,
192 &mut ambiguity_cache,
193 )
194 .await?;
195
196 changes.add_room(room_info);
197
198 if let Some(joined_room) = joined_room {
199 new_rooms.join.insert(room_id.clone(), joined_room);
200 }
201
202 if let Some(left_room) = left_room {
203 new_rooms.leave.insert(room_id.clone(), left_room);
204 }
205
206 if let Some(invited_room) = invited_room {
207 new_rooms.invite.insert(room_id.clone(), invited_room);
208 }
209
210 if let Some(knocked_room) = knocked_room {
211 new_rooms.knocked.insert(room_id.clone(), knocked_room);
212 }
213 }
214
215 for (room_id, raw) in &extensions.receipts.rooms {
220 match raw.deserialize() {
221 Ok(event) => {
222 changes.add_receipts(room_id, event.content);
223 }
224 Err(e) => {
225 let event_id: Option<String> = raw.get_field("event_id").ok().flatten();
226 #[rustfmt::skip]
227 warn!(
228 ?room_id, event_id,
229 "Failed to deserialize read receipt room event: {e}"
230 );
231 }
232 }
233
234 new_rooms
236 .join
237 .entry(room_id.to_owned())
238 .or_insert_with(JoinedRoomUpdate::default)
239 .ephemeral
240 .push(raw.clone().cast());
241 }
242
243 for (room_id, raw) in &extensions.typing.rooms {
244 new_rooms
246 .join
247 .entry(room_id.to_owned())
248 .or_insert_with(JoinedRoomUpdate::default)
249 .ephemeral
250 .push(raw.clone().cast());
251 }
252
253 for (room_id, raw) in &rooms_account_data {
255 self.handle_room_account_data(
256 room_id,
257 raw,
258 &mut changes,
259 &mut room_info_notable_updates,
260 )
261 .await;
262
263 if let Some(room) = self.state_store.room(room_id) {
264 match room.state() {
265 RoomState::Joined => new_rooms
266 .join
267 .entry(room_id.to_owned())
268 .or_insert_with(JoinedRoomUpdate::default)
269 .account_data
270 .append(&mut raw.to_vec()),
271 RoomState::Left | RoomState::Banned => new_rooms
272 .leave
273 .entry(room_id.to_owned())
274 .or_insert_with(LeftRoomUpdate::default)
275 .account_data
276 .append(&mut raw.to_vec()),
277 RoomState::Invited | RoomState::Knocked => {}
278 }
279 }
280 }
281
282 let user_id = &self.session_meta().expect("logged in user").user_id;
285
286 for (room_id, joined_room_update) in &mut new_rooms.join {
287 if let Some(mut room_info) = changes
288 .room_infos
289 .get(room_id)
290 .cloned()
291 .or_else(|| self.get_room(room_id).map(|r| r.clone_info()))
292 {
293 let prev_read_receipts = room_info.read_receipts.clone();
294
295 compute_unread_counts(
296 user_id,
297 room_id,
298 changes.receipts.get(room_id),
299 previous_events_provider.for_room(room_id),
300 &joined_room_update.timeline.events,
301 &mut room_info.read_receipts,
302 );
303
304 if prev_read_receipts != room_info.read_receipts {
305 room_info_notable_updates
306 .entry(room_id.clone())
307 .or_default()
308 .insert(RoomInfoNotableUpdateReasons::READ_RECEIPT);
309
310 changes.add_room(room_info);
311 }
312 }
313 }
314
315 account_data_processor.apply(&mut changes, &store).await;
316
317 changes.ambiguity_maps = ambiguity_cache.cache;
328
329 trace!("ready to submit changes to store");
330 let prev_ignored_user_list = self.load_previous_ignored_user_list().await;
331 store.save_changes(&changes).await?;
332 self.apply_changes(&changes, room_info_notable_updates, prev_ignored_user_list);
333 trace!("applied changes");
334
335 new_rooms.update_in_memory_caches(&self.state_store).await;
341
342 Ok(SyncResponse {
343 rooms: new_rooms,
344 notifications,
345 presence: Default::default(),
347 account_data: extensions.account_data.global.clone(),
348 to_device: Default::default(),
349 })
350 }
351
352 #[allow(clippy::too_many_arguments)]
353 async fn process_sliding_sync_room(
354 &self,
355 room_id: &RoomId,
356 requested_required_states: &[(StateEventType, String)],
357 room_data: &http::response::Room,
358 rooms_account_data: &mut BTreeMap<OwnedRoomId, Vec<Raw<AnyRoomAccountDataEvent>>>,
359 store: &BaseStateStore,
360 user_id: &UserId,
361 account_data_processor: &AccountDataProcessor,
362 changes: &mut StateChanges,
363 room_info_notable_updates: &mut BTreeMap<OwnedRoomId, RoomInfoNotableUpdateReasons>,
364 notifications: &mut BTreeMap<OwnedRoomId, Vec<Notification>>,
365 ambiguity_cache: &mut AmbiguityCache,
366 ) -> Result<(
367 RoomInfo,
368 Option<JoinedRoomUpdate>,
369 Option<LeftRoomUpdate>,
370 Option<InvitedRoom>,
371 Option<KnockedRoom>,
372 )> {
373 let (raw_state_events, state_events): (Vec<_>, Vec<_>) = {
374 let state_events = Self::deserialize_state_events(&room_data.required_state);
376
377 state_events.into_iter().unzip()
382 };
383
384 let is_new_room = !store.room_exists(room_id);
386
387 let stripped_state: Option<Vec<(Raw<AnyStrippedStateEvent>, AnyStrippedStateEvent)>> =
388 room_data
389 .invite_state
390 .as_ref()
391 .map(|invite_state| Self::deserialize_stripped_state_events(invite_state));
392
393 #[allow(unused_mut)] let (mut room, mut room_info, invited_room, knocked_room) = self
395 .process_sliding_sync_room_membership(
396 &state_events,
397 stripped_state.as_ref(),
398 store,
399 user_id,
400 room_id,
401 room_info_notable_updates,
402 );
403
404 room_info.mark_state_partially_synced();
405 room_info.handle_encryption_state(requested_required_states);
406
407 let mut user_ids = if !state_events.is_empty() {
408 self.handle_state(
409 &raw_state_events,
410 &state_events,
411 &mut room_info,
412 changes,
413 ambiguity_cache,
414 )
415 .await?
416 } else {
417 Default::default()
418 };
419
420 let push_rules = self.get_push_rules(account_data_processor).await?;
421
422 if let Some(invite_state) = &stripped_state {
424 self.handle_invited_state(
425 &room,
426 invite_state,
427 &push_rules,
428 &mut room_info,
429 changes,
430 notifications,
431 )
432 .await?;
433 }
434
435 process_room_properties(
436 room_id,
437 room_data,
438 &mut room_info,
439 is_new_room,
440 room_info_notable_updates,
441 );
442
443 let timeline = self
444 .handle_timeline(
445 &room,
446 room_data.limited,
447 room_data.timeline.clone(),
448 true,
449 room_data.prev_batch.clone(),
450 &push_rules,
451 &mut user_ids,
452 &mut room_info,
453 changes,
454 notifications,
455 ambiguity_cache,
456 )
457 .await?;
458
459 #[cfg(feature = "e2e-encryption")]
462 cache_latest_events(&room, &mut room_info, &timeline.events, Some(changes), Some(store))
463 .await;
464
465 #[cfg(feature = "e2e-encryption")]
466 if room_info.encryption_state().is_encrypted() {
467 if let Some(o) = self.olm_machine().await.as_ref() {
468 if !room.encryption_state().is_encrypted() {
469 let user_ids = store.get_user_ids(room_id, RoomMemberships::ACTIVE).await?;
473 o.update_tracked_users(user_ids.iter().map(Deref::deref)).await?
474 }
475
476 if !user_ids.is_empty() {
477 o.update_tracked_users(user_ids.iter().map(Deref::deref)).await?;
478 }
479 }
480 }
481
482 let notification_count = room_data.unread_notifications.clone().into();
483 room_info.update_notification_count(notification_count);
484
485 let ambiguity_changes = ambiguity_cache.changes.remove(room_id).unwrap_or_default();
486 let room_account_data = rooms_account_data.get(room_id).cloned();
487
488 match room_info.state() {
489 RoomState::Joined => {
490 let ephemeral = Vec::new();
494
495 Ok((
496 room_info,
497 Some(JoinedRoomUpdate::new(
498 timeline,
499 raw_state_events,
500 room_account_data.unwrap_or_default(),
501 ephemeral,
502 notification_count,
503 ambiguity_changes,
504 )),
505 None,
506 None,
507 None,
508 ))
509 }
510
511 RoomState::Left | RoomState::Banned => Ok((
512 room_info,
513 None,
514 Some(LeftRoomUpdate::new(
515 timeline,
516 raw_state_events,
517 room_account_data.unwrap_or_default(),
518 ambiguity_changes,
519 )),
520 None,
521 None,
522 )),
523
524 RoomState::Invited => Ok((room_info, None, None, invited_room, None)),
525
526 RoomState::Knocked => Ok((room_info, None, None, None, knocked_room)),
527 }
528 }
529
530 fn process_sliding_sync_room_membership(
536 &self,
537 state_events: &[AnySyncStateEvent],
538 stripped_state: Option<&Vec<(Raw<AnyStrippedStateEvent>, AnyStrippedStateEvent)>>,
539 store: &BaseStateStore,
540 user_id: &UserId,
541 room_id: &RoomId,
542 room_info_notable_updates: &mut BTreeMap<OwnedRoomId, RoomInfoNotableUpdateReasons>,
543 ) -> (Room, RoomInfo, Option<InvitedRoom>, Option<KnockedRoom>) {
544 if let Some(stripped_state) = stripped_state {
545 let room = store.get_or_create_room(
546 room_id,
547 RoomState::Invited,
548 self.room_info_notable_update_sender.clone(),
549 );
550 let mut room_info = room.clone_info();
551
552 let membership_event_content = stripped_state.iter().find_map(|(_, event)| {
555 if let AnyStrippedStateEvent::RoomMember(membership_event) = event {
556 if membership_event.state_key == user_id {
557 return Some(membership_event.content.clone());
558 }
559 }
560 None
561 });
562
563 if let Some(membership_event_content) = membership_event_content {
564 if membership_event_content.membership == MembershipState::Knock {
565 room_info.mark_as_knocked();
567 let raw_events = stripped_state.iter().map(|(raw, _)| raw.clone()).collect();
568 let knock_state = assign!(v3::KnockState::default(), { events: raw_events });
569 let knocked_room =
570 assign!(KnockedRoom::default(), { knock_state: knock_state });
571 return (room, room_info, None, Some(knocked_room));
572 }
573 }
574
575 room_info.mark_as_invited();
577 let raw_events = stripped_state.iter().map(|(raw, _)| raw.clone()).collect::<Vec<_>>();
578 let invited_room = InvitedRoom::from(v3::InviteState::from(raw_events));
579 (room, room_info, Some(invited_room), None)
580 } else {
581 let room = store.get_or_create_room(
582 room_id,
583 RoomState::Joined,
584 self.room_info_notable_update_sender.clone(),
585 );
586 let mut room_info = room.clone_info();
587
588 room_info.mark_as_joined();
593
594 self.handle_own_room_membership(
600 state_events,
601 &mut room_info,
602 room_info_notable_updates,
603 );
604
605 (room, room_info, None, None)
606 }
607 }
608
609 pub(crate) fn handle_own_room_membership(
612 &self,
613 state_events: &[AnySyncStateEvent],
614 room_info: &mut RoomInfo,
615 room_info_notable_updates: &mut BTreeMap<OwnedRoomId, RoomInfoNotableUpdateReasons>,
616 ) {
617 let Some(meta) = self.session_meta() else {
618 return;
619 };
620
621 for event in state_events.iter().rev() {
625 if let AnySyncStateEvent::RoomMember(member) = &event {
626 if member.state_key() == meta.user_id.as_str() {
629 let new_state: RoomState = member.membership().into();
630 if new_state != room_info.state() {
631 room_info.set_state(new_state);
632 room_info_notable_updates
634 .entry(room_info.room_id.to_owned())
635 .or_default()
636 .insert(RoomInfoNotableUpdateReasons::MEMBERSHIP);
637 }
638 break;
639 }
640 }
641 }
642 }
643}
644
645#[cfg(feature = "e2e-encryption")]
653async fn cache_latest_events(
654 room: &Room,
655 room_info: &mut RoomInfo,
656 events: &[TimelineEvent],
657 changes: Option<&StateChanges>,
658 store: Option<&BaseStateStore>,
659) {
660 use crate::{
661 deserialized_responses::DisplayName, store::ambiguity_map::is_display_name_ambiguous,
662 };
663
664 let mut encrypted_events =
665 Vec::with_capacity(room.latest_encrypted_events.read().unwrap().capacity());
666
667 let power_levels_from_changes = || {
669 let state_changes = changes?.state.get(room_info.room_id())?;
670 let room_power_levels_state =
671 state_changes.get(&StateEventType::RoomPowerLevels)?.values().next()?;
672 match room_power_levels_state.deserialize().ok()? {
673 AnySyncStateEvent::RoomPowerLevels(ev) => Some(ev.power_levels()),
674 _ => None,
675 }
676 };
677
678 let power_levels = match power_levels_from_changes() {
680 Some(power_levels) => Some(power_levels),
681 None => room.power_levels().await.ok(),
682 };
683
684 let power_levels_info = Some(room.own_user_id()).zip(power_levels.as_ref());
685
686 for event in events.iter().rev() {
687 if let Ok(timeline_event) = event.raw().deserialize() {
688 match is_suitable_for_latest_event(&timeline_event, power_levels_info) {
689 PossibleLatestEvent::YesRoomMessage(_)
690 | PossibleLatestEvent::YesPoll(_)
691 | PossibleLatestEvent::YesCallInvite(_)
692 | PossibleLatestEvent::YesCallNotify(_)
693 | PossibleLatestEvent::YesSticker(_)
694 | PossibleLatestEvent::YesKnockedStateEvent(_) => {
695 let mut sender_profile = None;
703 let mut sender_name_is_ambiguous = None;
704
705 if let Some(changes) = changes {
708 sender_profile = changes
709 .profiles
710 .get(room.room_id())
711 .and_then(|profiles_by_user| {
712 profiles_by_user.get(timeline_event.sender())
713 })
714 .cloned();
715
716 if let Some(sender_profile) = sender_profile.as_ref() {
717 sender_name_is_ambiguous = sender_profile
718 .as_original()
719 .and_then(|profile| profile.content.displayname.as_ref())
720 .and_then(|display_name| {
721 let display_name = DisplayName::new(display_name);
722
723 changes.ambiguity_maps.get(room.room_id()).and_then(
724 |map_for_room| {
725 map_for_room.get(&display_name).map(|users| {
726 is_display_name_ambiguous(&display_name, users)
727 })
728 },
729 )
730 });
731 }
732 }
733
734 if sender_profile.is_none() {
736 if let Some(store) = store {
737 sender_profile = store
738 .get_profile(room.room_id(), timeline_event.sender())
739 .await
740 .ok()
741 .flatten();
742
743 }
746 }
747
748 let latest_event = Box::new(LatestEvent::new_with_sender_details(
749 event.clone(),
750 sender_profile,
751 sender_name_is_ambiguous,
752 ));
753
754 room_info.latest_event = Some(latest_event);
756 room.latest_encrypted_events.write().unwrap().clear();
759 break;
762 }
763 PossibleLatestEvent::NoEncrypted => {
764 if encrypted_events.len() < encrypted_events.capacity() {
770 encrypted_events.push(event.raw().clone());
771 }
772 }
773 _ => {
774 }
776 }
777 } else {
778 warn!(
779 "Failed to deserialize event as AnySyncTimelineEvent. ID={}",
780 event.event_id().expect("Event has no ID!")
781 );
782 }
783 }
784
785 room.latest_encrypted_events.write().unwrap().extend(encrypted_events.into_iter().rev());
788}
789
790fn process_room_properties(
791 room_id: &RoomId,
792 room_data: &http::response::Room,
793 room_info: &mut RoomInfo,
794 is_new_room: bool,
795 room_info_notable_updates: &mut BTreeMap<OwnedRoomId, RoomInfoNotableUpdateReasons>,
796) {
797 match &room_data.avatar {
803 JsOption::Some(avatar_uri) => room_info.update_avatar(Some(avatar_uri.to_owned())),
805 JsOption::Null => room_info.update_avatar(None),
807 JsOption::Undefined => {}
809 }
810
811 if let Some(count) = room_data.joined_count {
815 room_info.update_joined_member_count(count.into());
816 }
817 if let Some(count) = room_data.invited_count {
818 room_info.update_invited_member_count(count.into());
819 }
820
821 if let Some(heroes) = &room_data.heroes {
822 room_info.update_heroes(
823 heroes
824 .iter()
825 .map(|hero| RoomHero {
826 user_id: hero.user_id.clone(),
827 display_name: hero.name.clone(),
828 avatar_url: hero.avatar.clone(),
829 })
830 .collect(),
831 );
832 }
833
834 room_info.set_prev_batch(room_data.prev_batch.as_deref());
835
836 if room_data.limited {
837 room_info.mark_members_missing();
838 }
839
840 if let Some(recency_stamp) = &room_data.bump_stamp {
841 let recency_stamp: u64 = (*recency_stamp).into();
842
843 if room_info.recency_stamp.as_ref() != Some(&recency_stamp) {
844 room_info.update_recency_stamp(recency_stamp);
845
846 if !is_new_room {
850 room_info_notable_updates
851 .entry(room_id.to_owned())
852 .or_default()
853 .insert(RoomInfoNotableUpdateReasons::RECENCY_STAMP);
854 }
855 }
856 }
857}
858
859#[cfg(all(test, not(target_family = "wasm")))]
860mod tests {
861 use std::collections::{BTreeMap, HashSet};
862 #[cfg(feature = "e2e-encryption")]
863 use std::sync::{Arc, RwLock as SyncRwLock};
864
865 use assert_matches::assert_matches;
866 use matrix_sdk_common::deserialized_responses::TimelineEvent;
867 #[cfg(feature = "e2e-encryption")]
868 use matrix_sdk_common::{
869 deserialized_responses::{UnableToDecryptInfo, UnableToDecryptReason},
870 ring_buffer::RingBuffer,
871 };
872 use matrix_sdk_test::async_test;
873 use ruma::{
874 api::client::sync::sync_events::UnreadNotificationsCount,
875 assign, event_id,
876 events::{
877 direct::{DirectEventContent, DirectUserIdentifier, OwnedDirectUserIdentifier},
878 room::{
879 avatar::RoomAvatarEventContent,
880 canonical_alias::RoomCanonicalAliasEventContent,
881 encryption::RoomEncryptionEventContent,
882 member::{MembershipState, RoomMemberEventContent},
883 message::SyncRoomMessageEvent,
884 name::RoomNameEventContent,
885 pinned_events::RoomPinnedEventsEventContent,
886 },
887 AnySyncMessageLikeEvent, AnySyncTimelineEvent, GlobalAccountDataEventContent,
888 StateEventContent, StateEventType,
889 },
890 mxc_uri, owned_event_id, owned_mxc_uri, owned_user_id, room_alias_id, room_id,
891 serde::Raw,
892 uint, user_id, JsOption, MxcUri, OwnedRoomId, OwnedUserId, RoomAliasId, RoomId, UserId,
893 };
894 use serde_json::json;
895
896 #[cfg(feature = "e2e-encryption")]
897 use super::cache_latest_events;
898 use super::http;
899 use crate::{
900 rooms::normal::{RoomHero, RoomInfoNotableUpdateReasons},
901 test_utils::logged_in_base_client,
902 BaseClient, EncryptionState, RequestedRequiredStates, RoomInfoNotableUpdate, RoomState,
903 };
904 #[cfg(feature = "e2e-encryption")]
905 use crate::{store::MemoryStore, Room};
906
907 #[async_test]
908 async fn test_notification_count_set() {
909 let client = logged_in_base_client(None).await;
910
911 let mut response = http::Response::new("42".to_owned());
912 let room_id = room_id!("!room:example.org");
913 let count = assign!(UnreadNotificationsCount::default(), {
914 highlight_count: Some(uint!(13)),
915 notification_count: Some(uint!(37)),
916 });
917
918 response.rooms.insert(
919 room_id.to_owned(),
920 assign!(http::response::Room::new(), {
921 unread_notifications: count.clone()
922 }),
923 );
924
925 let sync_response = client
926 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
927 .await
928 .expect("Failed to process sync");
929
930 let room = sync_response.rooms.join.get(room_id).unwrap();
932 assert_eq!(room.unread_notifications, count.clone().into());
933
934 let room = client.get_room(room_id).expect("found room");
936 assert_eq!(room.unread_notification_counts(), count.into());
937 }
938
939 #[async_test]
940 async fn test_can_process_empty_sliding_sync_response() {
941 let client = logged_in_base_client(None).await;
942 let empty_response = http::Response::new("5".to_owned());
943 client
944 .process_sliding_sync(&empty_response, &(), &RequestedRequiredStates::default())
945 .await
946 .expect("Failed to process sync");
947 }
948
949 #[async_test]
950 async fn test_room_with_unspecified_state_is_added_to_client_and_joined_list() {
951 let client = logged_in_base_client(None).await;
953 let room_id = room_id!("!r:e.uk");
954
955 let mut room = http::response::Room::new();
958 room.joined_count = Some(uint!(41));
959 let response = response_with_room(room_id, room);
960 let sync_resp = 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!(client_room.room_id(), room_id);
968 assert_eq!(client_room.joined_members_count(), 41);
969 assert_eq!(client_room.state(), RoomState::Joined);
970
971 assert!(sync_resp.rooms.join.contains_key(room_id));
973 assert!(!sync_resp.rooms.leave.contains_key(room_id));
974 assert!(!sync_resp.rooms.invite.contains_key(room_id));
975 }
976
977 #[async_test]
978 async fn test_missing_room_name_event() {
979 let client = logged_in_base_client(None).await;
981 let room_id = room_id!("!r:e.uk");
982
983 let mut room = http::response::Room::new();
986 room.name = Some("little room".to_owned());
987 let response = response_with_room(room_id, room);
988 let sync_resp = client
989 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
990 .await
991 .expect("Failed to process sync");
992
993 let client_room = client.get_room(room_id).expect("No room found");
995 assert!(client_room.name().is_none());
996 assert_eq!(client_room.compute_display_name().await.unwrap().to_string(), "Empty Room");
997 assert_eq!(client_room.state(), RoomState::Joined);
998
999 assert!(sync_resp.rooms.join.contains_key(room_id));
1001 assert!(!sync_resp.rooms.leave.contains_key(room_id));
1002 assert!(!sync_resp.rooms.invite.contains_key(room_id));
1003 assert!(!sync_resp.rooms.knocked.contains_key(room_id));
1004 }
1005
1006 #[async_test]
1007 async fn test_room_name_event() {
1008 let client = logged_in_base_client(None).await;
1010 let room_id = room_id!("!r:e.uk");
1011
1012 let mut room = http::response::Room::new();
1015
1016 room.name = Some("little room".to_owned());
1017 set_room_name(&mut room, user_id!("@a:b.c"), "The Name".to_owned());
1018
1019 let response = response_with_room(room_id, room);
1020 client
1021 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
1022 .await
1023 .expect("Failed to process sync");
1024
1025 let client_room = client.get_room(room_id).expect("No room found");
1027 assert_eq!(client_room.name().as_deref(), Some("The Name"));
1028 assert_eq!(client_room.compute_display_name().await.unwrap().to_string(), "The Name");
1029 }
1030
1031 #[async_test]
1032 async fn test_missing_invited_room_name_event() {
1033 let client = logged_in_base_client(None).await;
1035 let room_id = room_id!("!r:e.uk");
1036 let user_id = user_id!("@w:e.uk");
1037 let inviter = user_id!("@john:mastodon.org");
1038
1039 let mut room = http::response::Room::new();
1042 set_room_invited(&mut room, inviter, user_id);
1043 room.name = Some("name from sliding sync response".to_owned());
1044 let response = response_with_room(room_id, room);
1045 let sync_resp = client
1046 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
1047 .await
1048 .expect("Failed to process sync");
1049
1050 let client_room = client.get_room(room_id).expect("No room found");
1052 assert!(client_room.name().is_none());
1053
1054 assert_eq!(client_room.compute_display_name().await.unwrap().to_string(), "w");
1056
1057 assert_eq!(client_room.state(), RoomState::Invited);
1058
1059 assert!(!sync_resp.rooms.join.contains_key(room_id));
1061 assert!(!sync_resp.rooms.leave.contains_key(room_id));
1062 assert!(sync_resp.rooms.invite.contains_key(room_id));
1063 assert!(!sync_resp.rooms.knocked.contains_key(room_id));
1064 }
1065
1066 #[async_test]
1067 async fn test_invited_room_name_event() {
1068 let client = logged_in_base_client(None).await;
1070 let room_id = room_id!("!r:e.uk");
1071 let user_id = user_id!("@w:e.uk");
1072 let inviter = user_id!("@john:mastodon.org");
1073
1074 let mut room = http::response::Room::new();
1077
1078 set_room_invited(&mut room, inviter, user_id);
1079
1080 room.name = Some("name from sliding sync response".to_owned());
1081 set_room_name(&mut room, user_id!("@a:b.c"), "The Name".to_owned());
1082
1083 let response = response_with_room(room_id, room);
1084 client
1085 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
1086 .await
1087 .expect("Failed to process sync");
1088
1089 let client_room = client.get_room(room_id).expect("No room found");
1091 assert_eq!(client_room.name().as_deref(), Some("The Name"));
1092 assert_eq!(client_room.compute_display_name().await.unwrap().to_string(), "The Name");
1093 }
1094
1095 #[async_test]
1096 async fn test_receiving_a_knocked_room_membership_event_creates_a_knocked_room() {
1097 let client = logged_in_base_client(None).await;
1099 let room_id = room_id!("!r:e.uk");
1100 let user_id = client.session_meta().unwrap().user_id.to_owned();
1101
1102 let mut room = http::response::Room::new();
1105 set_room_knocked(&mut room, &user_id);
1106
1107 let response = response_with_room(room_id, room);
1108 client
1109 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
1110 .await
1111 .expect("Failed to process sync");
1112
1113 let client_room = client.get_room(room_id).expect("No room found");
1115 assert_eq!(client_room.state(), RoomState::Knocked);
1116 }
1117
1118 #[async_test]
1119 async fn test_receiving_a_knocked_room_membership_event_with_wrong_state_key_creates_an_invited_room(
1120 ) {
1121 let client = logged_in_base_client(None).await;
1123 let room_id = room_id!("!r:e.uk");
1124 let user_id = user_id!("@w:e.uk");
1125
1126 let mut room = http::response::Room::new();
1128 set_room_knocked(&mut room, user_id);
1129
1130 let response = response_with_room(room_id, room);
1131 client
1132 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
1133 .await
1134 .expect("Failed to process sync");
1135
1136 let client_room = client.get_room(room_id).expect("No room found");
1139 assert_eq!(client_room.state(), RoomState::Invited);
1140 }
1141
1142 #[async_test]
1143 async fn test_receiving_an_unknown_room_membership_event_in_invite_state_creates_an_invited_room(
1144 ) {
1145 let client = logged_in_base_client(None).await;
1147 let room_id = room_id!("!r:e.uk");
1148 let user_id = client.session_meta().unwrap().user_id.to_owned();
1149
1150 let mut room = http::response::Room::new();
1152 let event = Raw::new(&json!({
1153 "type": "m.room.member",
1154 "sender": user_id,
1155 "content": {
1156 "is_direct": true,
1157 "membership": "join",
1158 },
1159 "state_key": user_id,
1160 }))
1161 .expect("Failed to make raw event")
1162 .cast();
1163 room.invite_state = Some(vec![event]);
1164
1165 let response = response_with_room(room_id, room);
1166 client
1167 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
1168 .await
1169 .expect("Failed to process sync");
1170
1171 let client_room = client.get_room(room_id).expect("No room found");
1173 assert_eq!(client_room.state(), RoomState::Invited);
1174 }
1175
1176 #[async_test]
1177 async fn test_left_a_room_from_required_state_event() {
1178 let client = logged_in_base_client(None).await;
1180 let room_id = room_id!("!r:e.uk");
1181 let user_id = user_id!("@u:e.uk");
1182
1183 let mut room = http::response::Room::new();
1185 set_room_joined(&mut room, user_id);
1186 let response = response_with_room(room_id, room);
1187 client
1188 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
1189 .await
1190 .expect("Failed to process sync");
1191 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Joined);
1192
1193 let mut room = http::response::Room::new();
1195 set_room_left(&mut room, user_id);
1196 let response = response_with_room(room_id, room);
1197 let sync_resp = client
1198 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
1199 .await
1200 .expect("Failed to process sync");
1201
1202 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Left);
1204
1205 assert!(!sync_resp.rooms.join.contains_key(room_id));
1207 assert!(sync_resp.rooms.leave.contains_key(room_id));
1208 assert!(!sync_resp.rooms.invite.contains_key(room_id));
1209 assert!(!sync_resp.rooms.knocked.contains_key(room_id));
1210 }
1211
1212 #[async_test]
1213 async fn test_kick_or_ban_updates_room_to_left() {
1214 for membership in [MembershipState::Leave, MembershipState::Ban] {
1215 let room_id = room_id!("!r:e.uk");
1216 let user_a_id = user_id!("@a:e.uk");
1217 let user_b_id = user_id!("@b:e.uk");
1218 let client = logged_in_base_client(Some(user_a_id)).await;
1219
1220 let mut room = http::response::Room::new();
1222 set_room_joined(&mut room, user_a_id);
1223 let response = response_with_room(room_id, room);
1224 client
1225 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
1226 .await
1227 .expect("Failed to process sync");
1228 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Joined);
1229
1230 let mut room = http::response::Room::new();
1232 room.required_state.push(make_state_event(
1233 user_b_id,
1234 user_a_id.as_str(),
1235 RoomMemberEventContent::new(membership.clone()),
1236 None,
1237 ));
1238 let response = response_with_room(room_id, room);
1239 let sync_resp = client
1240 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
1241 .await
1242 .expect("Failed to process sync");
1243
1244 match membership {
1245 MembershipState::Leave => {
1246 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Left);
1248 }
1249 MembershipState::Ban => {
1250 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Banned);
1252 }
1253 _ => panic!("Unexpected membership state found: {membership}"),
1254 }
1255
1256 assert!(!sync_resp.rooms.join.contains_key(room_id));
1258 assert!(sync_resp.rooms.leave.contains_key(room_id));
1259 assert!(!sync_resp.rooms.invite.contains_key(room_id));
1260 assert!(!sync_resp.rooms.knocked.contains_key(room_id));
1261 }
1262 }
1263
1264 #[async_test]
1265 async fn test_left_a_room_from_timeline_state_event() {
1266 let client = logged_in_base_client(None).await;
1268 let room_id = room_id!("!r:e.uk");
1269 let user_id = user_id!("@u:e.uk");
1270
1271 let mut room = http::response::Room::new();
1273 set_room_joined(&mut room, user_id);
1274 let response = response_with_room(room_id, room);
1275 client
1276 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
1277 .await
1278 .expect("Failed to process sync");
1279 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Joined);
1280
1281 let mut room = http::response::Room::new();
1283 set_room_left_as_timeline_event(&mut room, user_id);
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 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Joined);
1292 }
1293
1294 #[async_test]
1295 async fn test_can_be_reinvited_to_a_left_room() {
1296 let client = logged_in_base_client(None).await;
1300 let room_id = room_id!("!r:e.uk");
1301 let user_id = user_id!("@u:e.uk");
1302
1303 let mut room = http::response::Room::new();
1305 set_room_joined(&mut room, user_id);
1306 let response = response_with_room(room_id, room);
1307 client
1308 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
1309 .await
1310 .expect("Failed to process sync");
1311 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Joined);
1313
1314 let mut room = http::response::Room::new();
1316 set_room_left(&mut room, user_id);
1317 let response = response_with_room(room_id, room);
1318 client
1319 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
1320 .await
1321 .expect("Failed to process sync");
1322 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Left);
1324
1325 let mut room = http::response::Room::new();
1327 set_room_invited(&mut room, user_id, user_id);
1328 let response = response_with_room(room_id, room);
1329 client
1330 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
1331 .await
1332 .expect("Failed to process sync");
1333
1334 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Invited);
1336 }
1337
1338 #[async_test]
1339 async fn test_other_person_leaving_a_dm_is_reflected_in_their_membership_and_direct_targets() {
1340 let room_id = room_id!("!r:e.uk");
1341 let user_a_id = user_id!("@a:e.uk");
1342 let user_b_id = user_id!("@b:e.uk");
1343
1344 let client = logged_in_base_client(None).await;
1346 create_dm(&client, room_id, user_a_id, user_b_id, MembershipState::Join).await;
1347
1348 assert!(direct_targets(&client, room_id).contains(<&DirectUserIdentifier>::from(user_b_id)));
1350 assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Join);
1351
1352 update_room_membership(&client, room_id, user_b_id, MembershipState::Leave).await;
1354
1355 assert!(direct_targets(&client, room_id).contains(<&DirectUserIdentifier>::from(user_b_id)));
1359 assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Leave);
1360 }
1361
1362 #[async_test]
1363 async fn test_other_person_refusing_invite_to_a_dm_is_reflected_in_their_membership_and_direct_targets(
1364 ) {
1365 let room_id = room_id!("!r:e.uk");
1366 let user_a_id = user_id!("@a:e.uk");
1367 let user_b_id = user_id!("@b:e.uk");
1368
1369 let client = logged_in_base_client(None).await;
1371 create_dm(&client, room_id, user_a_id, user_b_id, MembershipState::Invite).await;
1372
1373 assert!(direct_targets(&client, room_id).contains(<&DirectUserIdentifier>::from(user_b_id)));
1375 assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Invite);
1376
1377 update_room_membership(&client, room_id, user_b_id, MembershipState::Leave).await;
1379
1380 assert!(direct_targets(&client, room_id).contains(<&DirectUserIdentifier>::from(user_b_id)));
1384 assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Leave);
1385 }
1386
1387 #[async_test]
1388 async fn test_members_count_in_a_dm_where_other_person_has_joined() {
1389 let room_id = room_id!("!r:bar.org");
1390 let user_a_id = user_id!("@a:bar.org");
1391 let user_b_id = user_id!("@b:bar.org");
1392
1393 let client = logged_in_base_client(None).await;
1395 create_dm(&client, room_id, user_a_id, user_b_id, MembershipState::Join).await;
1396
1397 assert_eq!(membership(&client, room_id, user_a_id).await, MembershipState::Join);
1399
1400 assert!(direct_targets(&client, room_id).contains(<&DirectUserIdentifier>::from(user_b_id)));
1402 assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Join);
1403
1404 let room = client.get_room(room_id).unwrap();
1405
1406 assert_eq!(room.active_members_count(), 2);
1407 assert_eq!(room.joined_members_count(), 2);
1408 assert_eq!(room.invited_members_count(), 0);
1409 }
1410
1411 #[async_test]
1412 async fn test_members_count_in_a_dm_where_other_person_is_invited() {
1413 let room_id = room_id!("!r:bar.org");
1414 let user_a_id = user_id!("@a:bar.org");
1415 let user_b_id = user_id!("@b:bar.org");
1416
1417 let client = logged_in_base_client(None).await;
1419 create_dm(&client, room_id, user_a_id, user_b_id, MembershipState::Invite).await;
1420
1421 assert_eq!(membership(&client, room_id, user_a_id).await, MembershipState::Join);
1423
1424 assert!(direct_targets(&client, room_id).contains(<&DirectUserIdentifier>::from(user_b_id)));
1426 assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Invite);
1427
1428 let room = client.get_room(room_id).unwrap();
1429
1430 assert_eq!(room.active_members_count(), 2);
1431 assert_eq!(room.joined_members_count(), 1);
1432 assert_eq!(room.invited_members_count(), 1);
1433 }
1434
1435 #[async_test]
1436 async fn test_avatar_is_found_when_processing_sliding_sync_response() {
1437 let client = logged_in_base_client(None).await;
1439 let room_id = room_id!("!r:e.uk");
1440
1441 let room = {
1443 let mut room = http::response::Room::new();
1444 room.avatar = JsOption::from_option(Some(mxc_uri!("mxc://e.uk/med1").to_owned()));
1445
1446 room
1447 };
1448 let response = response_with_room(room_id, room);
1449 client
1450 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
1451 .await
1452 .expect("Failed to process sync");
1453
1454 let client_room = client.get_room(room_id).expect("No room found");
1456 assert_eq!(
1457 client_room.avatar_url().expect("No avatar URL").media_id().expect("No media ID"),
1458 "med1"
1459 );
1460 }
1461
1462 #[async_test]
1463 async fn test_avatar_can_be_unset_when_processing_sliding_sync_response() {
1464 let client = logged_in_base_client(None).await;
1466 let room_id = room_id!("!r:e.uk");
1467
1468 let room = {
1472 let mut room = http::response::Room::new();
1473 room.avatar = JsOption::from_option(Some(mxc_uri!("mxc://e.uk/med1").to_owned()));
1474
1475 room
1476 };
1477 let response = response_with_room(room_id, room);
1478 client
1479 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
1480 .await
1481 .expect("Failed to process sync");
1482
1483 let client_room = client.get_room(room_id).expect("No room found");
1485 assert_eq!(
1486 client_room.avatar_url().expect("No avatar URL").media_id().expect("No media ID"),
1487 "med1"
1488 );
1489
1490 let room = http::response::Room::new();
1494 let response = response_with_room(room_id, room);
1495 client
1496 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
1497 .await
1498 .expect("Failed to process sync");
1499
1500 let client_room = client.get_room(room_id).expect("No room found");
1502 assert_eq!(
1503 client_room.avatar_url().expect("No avatar URL").media_id().expect("No media ID"),
1504 "med1"
1505 );
1506
1507 let room = {
1511 let mut room = http::response::Room::new();
1512 room.avatar = JsOption::Null;
1513
1514 room
1515 };
1516 let response = response_with_room(room_id, room);
1517 client
1518 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
1519 .await
1520 .expect("Failed to process sync");
1521
1522 let client_room = client.get_room(room_id).expect("No room found");
1524 assert!(client_room.avatar_url().is_none());
1525 }
1526
1527 #[async_test]
1528 async fn test_avatar_is_found_from_required_state_when_processing_sliding_sync_response() {
1529 let client = logged_in_base_client(None).await;
1531 let room_id = room_id!("!r:e.uk");
1532 let user_id = user_id!("@u:e.uk");
1533
1534 let room = room_with_avatar(mxc_uri!("mxc://e.uk/med1"), user_id);
1536 let response = response_with_room(room_id, room);
1537 client
1538 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
1539 .await
1540 .expect("Failed to process sync");
1541
1542 let client_room = client.get_room(room_id).expect("No room found");
1544 assert_eq!(
1545 client_room.avatar_url().expect("No avatar URL").media_id().expect("No media ID"),
1546 "med1"
1547 );
1548 }
1549
1550 #[async_test]
1551 async fn test_invitation_room_is_added_to_client_and_invite_list() {
1552 let client = logged_in_base_client(None).await;
1554 let room_id = room_id!("!r:e.uk");
1555 let user_id = user_id!("@u:e.uk");
1556
1557 let mut room = http::response::Room::new();
1559 set_room_invited(&mut room, user_id, user_id);
1560 let response = response_with_room(room_id, room);
1561 let sync_resp = client
1562 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
1563 .await
1564 .expect("Failed to process sync");
1565
1566 let client_room = client.get_room(room_id).expect("No room found");
1568 assert_eq!(client_room.room_id(), room_id);
1569 assert_eq!(client_room.state(), RoomState::Invited);
1570
1571 assert!(!sync_resp.rooms.invite[room_id].invite_state.is_empty());
1573 assert!(!sync_resp.rooms.join.contains_key(room_id));
1574 }
1575
1576 #[async_test]
1577 async fn test_avatar_is_found_in_invitation_room_when_processing_sliding_sync_response() {
1578 let client = logged_in_base_client(None).await;
1580 let room_id = room_id!("!r:e.uk");
1581 let user_id = user_id!("@u:e.uk");
1582
1583 let mut room = room_with_avatar(mxc_uri!("mxc://e.uk/med1"), user_id);
1585 set_room_invited(&mut room, user_id, user_id);
1586 let response = response_with_room(room_id, room);
1587 client
1588 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
1589 .await
1590 .expect("Failed to process sync");
1591
1592 let client_room = client.get_room(room_id).expect("No room found");
1594 assert_eq!(
1595 client_room.avatar_url().expect("No avatar URL").media_id().expect("No media ID"),
1596 "med1"
1597 );
1598 }
1599
1600 #[async_test]
1601 async fn test_canonical_alias_is_found_in_invitation_room_when_processing_sliding_sync_response(
1602 ) {
1603 let client = logged_in_base_client(None).await;
1605 let room_id = room_id!("!r:e.uk");
1606 let user_id = user_id!("@u:e.uk");
1607 let room_alias_id = room_alias_id!("#myroom:e.uk");
1608
1609 let mut room = room_with_canonical_alias(room_alias_id, user_id);
1611 set_room_invited(&mut room, user_id, user_id);
1612 let response = response_with_room(room_id, room);
1613 client
1614 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
1615 .await
1616 .expect("Failed to process sync");
1617
1618 let client_room = client.get_room(room_id).expect("No room found");
1620 assert_eq!(client_room.canonical_alias(), Some(room_alias_id.to_owned()));
1621 }
1622
1623 #[async_test]
1624 async fn test_display_name_from_sliding_sync_doesnt_override_alias() {
1625 let client = logged_in_base_client(None).await;
1627 let room_id = room_id!("!r:e.uk");
1628 let user_id = user_id!("@u:e.uk");
1629 let room_alias_id = room_alias_id!("#myroom:e.uk");
1630
1631 let mut room = room_with_canonical_alias(room_alias_id, user_id);
1634 room.name = Some("This came from the server".to_owned());
1635 let response = response_with_room(room_id, room);
1636 client
1637 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
1638 .await
1639 .expect("Failed to process sync");
1640
1641 let client_room = client.get_room(room_id).expect("No room found");
1643 assert_eq!(client_room.compute_display_name().await.unwrap().to_string(), "myroom");
1644 assert!(client_room.name().is_none());
1645 }
1646
1647 #[async_test]
1648 async fn test_compute_heroes_from_sliding_sync() {
1649 let client = logged_in_base_client(None).await;
1651 let room_id = room_id!("!r:e.uk");
1652 let gordon = user_id!("@gordon:e.uk").to_owned();
1653 let alice = user_id!("@alice:e.uk").to_owned();
1654
1655 let mut room = http::response::Room::new();
1658 room.heroes = Some(vec![
1659 assign!(http::response::Hero::new(gordon), {
1660 name: Some("Gordon".to_owned()),
1661 }),
1662 assign!(http::response::Hero::new(alice), {
1663 name: Some("Alice".to_owned()),
1664 avatar: Some(owned_mxc_uri!("mxc://e.uk/med1"))
1665 }),
1666 ]);
1667 let response = response_with_room(room_id, room);
1668 let _sync_resp = client
1669 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
1670 .await
1671 .expect("Failed to process sync");
1672
1673 let client_room = client.get_room(room_id).expect("No room found");
1675 assert_eq!(client_room.room_id(), room_id);
1676 assert_eq!(client_room.state(), RoomState::Joined);
1677
1678 assert_eq!(
1680 client_room.clone_info().summary.heroes(),
1681 &[
1682 RoomHero {
1683 user_id: owned_user_id!("@gordon:e.uk"),
1684 display_name: Some("Gordon".to_owned()),
1685 avatar_url: None
1686 },
1687 RoomHero {
1688 user_id: owned_user_id!("@alice:e.uk"),
1689 display_name: Some("Alice".to_owned()),
1690 avatar_url: Some(owned_mxc_uri!("mxc://e.uk/med1"))
1691 },
1692 ]
1693 );
1694 }
1695
1696 #[async_test]
1697 async fn test_last_event_from_sliding_sync_is_cached() {
1698 let client = logged_in_base_client(None).await;
1700 let room_id = room_id!("!r:e.uk");
1701 let event_a = json!({
1702 "sender":"@alice:example.com",
1703 "type":"m.room.message",
1704 "event_id": "$ida",
1705 "origin_server_ts": 12344446,
1706 "content":{"body":"A", "msgtype": "m.text"}
1707 });
1708 let event_b = json!({
1709 "sender":"@alice:example.com",
1710 "type":"m.room.message",
1711 "event_id": "$idb",
1712 "origin_server_ts": 12344447,
1713 "content":{"body":"B", "msgtype": "m.text"}
1714 });
1715
1716 let events = &[event_a, event_b.clone()];
1718 let room = room_with_timeline(events);
1719 let response = response_with_room(room_id, room);
1720 client
1721 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
1722 .await
1723 .expect("Failed to process sync");
1724
1725 let client_room = client.get_room(room_id).expect("No room found");
1727 assert_eq!(
1728 ev_id(client_room.latest_event().map(|latest_event| latest_event.event().clone())),
1729 "$idb"
1730 );
1731 }
1732
1733 #[async_test]
1734 async fn test_last_knock_event_from_sliding_sync_is_cached_if_user_has_permissions() {
1735 let own_user_id = user_id!("@me:e.uk");
1736 let client = logged_in_base_client(Some(own_user_id)).await;
1738 let room_id = room_id!("!r:e.uk");
1739
1740 let power_levels = json!({
1742 "sender":"@alice:example.com",
1743 "state_key":"",
1744 "type":"m.room.power_levels",
1745 "event_id": "$idb",
1746 "origin_server_ts": 12344445,
1747 "content":{ "invite": 100, "kick": 100, "users": { own_user_id: 100 } },
1748 "room_id": room_id,
1749 });
1750
1751 let knock_event = json!({
1753 "sender":"@alice:example.com",
1754 "state_key":"@alice:example.com",
1755 "type":"m.room.member",
1756 "event_id": "$ida",
1757 "origin_server_ts": 12344446,
1758 "content":{"membership": "knock"},
1759 "room_id": room_id,
1760 });
1761
1762 let events = &[knock_event];
1764 let mut room = room_with_timeline(events);
1765 room.required_state.push(Raw::new(&power_levels).unwrap().cast());
1766 let response = response_with_room(room_id, room);
1767 client
1768 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
1769 .await
1770 .expect("Failed to process sync");
1771
1772 let client_room = client.get_room(room_id).expect("No room found");
1774 assert_eq!(
1775 ev_id(client_room.latest_event().map(|latest_event| latest_event.event().clone())),
1776 "$ida"
1777 );
1778 }
1779
1780 #[async_test]
1781 async fn test_last_knock_event_from_sliding_sync_is_not_cached_without_permissions() {
1782 let own_user_id = user_id!("@me:e.uk");
1783 let client = logged_in_base_client(Some(own_user_id)).await;
1785 let room_id = room_id!("!r:e.uk");
1786
1787 let power_levels = json!({
1790 "sender":"@alice:example.com",
1791 "state_key":"",
1792 "type":"m.room.power_levels",
1793 "event_id": "$idb",
1794 "origin_server_ts": 12344445,
1795 "content":{ "invite": 50, "kick": 50, "users": { own_user_id: 0 } },
1796 "room_id": room_id,
1797 });
1798
1799 let knock_event = json!({
1801 "sender":"@alice:example.com",
1802 "state_key":"@alice:example.com",
1803 "type":"m.room.member",
1804 "event_id": "$ida",
1805 "origin_server_ts": 12344446,
1806 "content":{"membership": "knock"},
1807 "room_id": room_id,
1808 });
1809
1810 let events = &[knock_event];
1812 let mut room = room_with_timeline(events);
1813 room.required_state.push(Raw::new(&power_levels).unwrap().cast());
1814 let response = response_with_room(room_id, room);
1815 client
1816 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
1817 .await
1818 .expect("Failed to process sync");
1819
1820 let client_room = client.get_room(room_id).expect("No room found");
1822 assert!(client_room.latest_event().is_none());
1823 }
1824
1825 #[async_test]
1826 async fn test_last_non_knock_member_state_event_from_sliding_sync_is_not_cached() {
1827 let client = logged_in_base_client(None).await;
1829 let room_id = room_id!("!r:e.uk");
1830 let join_event = json!({
1832 "sender":"@alice:example.com",
1833 "state_key":"@alice:example.com",
1834 "type":"m.room.member",
1835 "event_id": "$ida",
1836 "origin_server_ts": 12344446,
1837 "content":{"membership": "join"},
1838 "room_id": room_id,
1839 });
1840
1841 let events = &[join_event];
1843 let room = room_with_timeline(events);
1844 let response = response_with_room(room_id, room);
1845 client
1846 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
1847 .await
1848 .expect("Failed to process sync");
1849
1850 let client_room = client.get_room(room_id).expect("No room found");
1852 assert!(client_room.latest_event().is_none());
1853 }
1854
1855 #[async_test]
1856 async fn test_cached_latest_event_can_be_redacted() {
1857 let client = logged_in_base_client(None).await;
1859 let room_id = room_id!("!r:e.uk");
1860 let event_a = json!({
1861 "sender": "@alice:example.com",
1862 "type": "m.room.message",
1863 "event_id": "$ida",
1864 "origin_server_ts": 12344446,
1865 "content": { "body":"A", "msgtype": "m.text" },
1866 });
1867
1868 let room = room_with_timeline(&[event_a]);
1870 let response = response_with_room(room_id, room);
1871 client
1872 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
1873 .await
1874 .expect("Failed to process sync");
1875
1876 let client_room = client.get_room(room_id).expect("No room found");
1878 assert_eq!(
1879 ev_id(client_room.latest_event().map(|latest_event| latest_event.event().clone())),
1880 "$ida"
1881 );
1882
1883 let redaction = json!({
1884 "sender": "@alice:example.com",
1885 "type": "m.room.redaction",
1886 "event_id": "$idb",
1887 "redacts": "$ida",
1888 "origin_server_ts": 12344448,
1889 "content": {},
1890 });
1891
1892 let room = room_with_timeline(&[redaction]);
1894 let response = response_with_room(room_id, room);
1895 client
1896 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
1897 .await
1898 .expect("Failed to process sync");
1899
1900 let client_room = client.get_room(room_id).expect("No room found");
1902 let latest_event = client_room.latest_event().unwrap();
1903 assert_eq!(latest_event.event_id().unwrap(), "$ida");
1904
1905 assert_matches!(
1907 latest_event.event().raw().deserialize().unwrap(),
1908 AnySyncTimelineEvent::MessageLike(AnySyncMessageLikeEvent::RoomMessage(
1909 SyncRoomMessageEvent::Redacted(_)
1910 ))
1911 );
1912 }
1913
1914 #[cfg(feature = "e2e-encryption")]
1915 #[async_test]
1916 async fn test_when_no_events_we_dont_cache_any() {
1917 let events = &[];
1918 let chosen = choose_event_to_cache(events).await;
1919 assert!(chosen.is_none());
1920 }
1921
1922 #[cfg(feature = "e2e-encryption")]
1923 #[async_test]
1924 async fn test_when_only_one_event_we_cache_it() {
1925 let event1 = make_event("m.room.message", "$1");
1926 let events = &[event1.clone()];
1927 let chosen = choose_event_to_cache(events).await;
1928 assert_eq!(ev_id(chosen), rawev_id(event1));
1929 }
1930
1931 #[cfg(feature = "e2e-encryption")]
1932 #[async_test]
1933 async fn test_with_multiple_events_we_cache_the_last_one() {
1934 let event1 = make_event("m.room.message", "$1");
1935 let event2 = make_event("m.room.message", "$2");
1936 let events = &[event1, event2.clone()];
1937 let chosen = choose_event_to_cache(events).await;
1938 assert_eq!(ev_id(chosen), rawev_id(event2));
1939 }
1940
1941 #[cfg(feature = "e2e-encryption")]
1942 #[async_test]
1943 async fn test_cache_the_latest_relevant_event_and_ignore_irrelevant_ones_even_if_later() {
1944 let event1 = make_event("m.room.message", "$1");
1945 let event2 = make_event("m.room.message", "$2");
1946 let event3 = make_event("m.room.powerlevels", "$3");
1947 let event4 = make_event("m.room.powerlevels", "$5");
1948 let events = &[event1, event2.clone(), event3, event4];
1949 let chosen = choose_event_to_cache(events).await;
1950 assert_eq!(ev_id(chosen), rawev_id(event2));
1951 }
1952
1953 #[cfg(feature = "e2e-encryption")]
1954 #[async_test]
1955 async fn test_prefer_to_cache_nothing_rather_than_irrelevant_events() {
1956 let event1 = make_event("m.room.power_levels", "$1");
1957 let events = &[event1];
1958 let chosen = choose_event_to_cache(events).await;
1959 assert!(chosen.is_none());
1960 }
1961
1962 #[cfg(feature = "e2e-encryption")]
1963 #[async_test]
1964 async fn test_cache_encrypted_events_that_are_after_latest_message() {
1965 let event1 = make_event("m.room.message", "$1");
1967 let event2 = make_event("m.room.message", "$2");
1968 let event3 = make_encrypted_event("$3");
1969 let event4 = make_encrypted_event("$4");
1970 let events = &[event1, event2.clone(), event3.clone(), event4.clone()];
1971
1972 let room = make_room();
1974 let mut room_info = room.clone_info();
1975 cache_latest_events(&room, &mut room_info, events, None, None).await;
1976
1977 assert_eq!(
1979 ev_id(room_info.latest_event.as_ref().map(|latest_event| latest_event.event().clone())),
1980 rawev_id(event2.clone())
1981 );
1982
1983 room.set_room_info(room_info, RoomInfoNotableUpdateReasons::empty());
1984 assert_eq!(
1985 ev_id(room.latest_event().map(|latest_event| latest_event.event().clone())),
1986 rawev_id(event2)
1987 );
1988
1989 assert_eq!(rawevs_ids(&room.latest_encrypted_events), evs_ids(&[event3, event4]));
1991 }
1992
1993 #[cfg(feature = "e2e-encryption")]
1994 #[async_test]
1995 async fn test_dont_cache_encrypted_events_that_are_before_latest_message() {
1996 let event1 = make_encrypted_event("$1");
1998 let event2 = make_event("m.room.message", "$2");
1999 let event3 = make_encrypted_event("$3");
2000 let events = &[event1, event2.clone(), event3.clone()];
2001
2002 let room = make_room();
2004 let mut room_info = room.clone_info();
2005 cache_latest_events(&room, &mut room_info, events, None, None).await;
2006 room.set_room_info(room_info, RoomInfoNotableUpdateReasons::empty());
2007
2008 assert_eq!(
2010 ev_id(room.latest_event().map(|latest_event| latest_event.event().clone())),
2011 rawev_id(event2)
2012 );
2013
2014 assert_eq!(rawevs_ids(&room.latest_encrypted_events), evs_ids(&[event3]));
2016 }
2017
2018 #[cfg(feature = "e2e-encryption")]
2019 #[async_test]
2020 async fn test_skip_irrelevant_events_eg_receipts_even_if_after_message() {
2021 let event1 = make_event("m.room.message", "$1");
2024 let event2 = make_event("m.room.message", "$2");
2025 let event3 = make_encrypted_event("$3");
2026 let event4 = make_event("m.read", "$4");
2027 let event5 = make_encrypted_event("$5");
2028 let events = &[event1, event2.clone(), event3.clone(), event4, event5.clone()];
2029
2030 let room = make_room();
2032 let mut room_info = room.clone_info();
2033 cache_latest_events(&room, &mut room_info, events, None, None).await;
2034 room.set_room_info(room_info, RoomInfoNotableUpdateReasons::empty());
2035
2036 assert_eq!(
2038 ev_id(room.latest_event().map(|latest_event| latest_event.event().clone())),
2039 rawev_id(event2)
2040 );
2041
2042 assert_eq!(rawevs_ids(&room.latest_encrypted_events), evs_ids(&[event3, event5]));
2044 }
2045
2046 #[cfg(feature = "e2e-encryption")]
2047 #[async_test]
2048 async fn test_only_store_the_max_number_of_encrypted_events() {
2049 let evente = make_event("m.room.message", "$e");
2052 let eventd = make_event("m.room.message", "$d");
2053 let eventc = make_encrypted_event("$c");
2054 let event9 = make_encrypted_event("$9");
2055 let event8 = make_encrypted_event("$8");
2056 let event7 = make_encrypted_event("$7");
2057 let eventb = make_event("m.read", "$b");
2058 let event6 = make_encrypted_event("$6");
2059 let event5 = make_encrypted_event("$5");
2060 let event4 = make_encrypted_event("$4");
2061 let event3 = make_encrypted_event("$3");
2062 let event2 = make_encrypted_event("$2");
2063 let eventa = make_event("m.read", "$a");
2064 let event1 = make_encrypted_event("$1");
2065 let event0 = make_encrypted_event("$0");
2066 let events = &[
2067 evente,
2068 eventd.clone(),
2069 eventc,
2070 event9.clone(),
2071 event8.clone(),
2072 event7.clone(),
2073 eventb,
2074 event6.clone(),
2075 event5.clone(),
2076 event4.clone(),
2077 event3.clone(),
2078 event2.clone(),
2079 eventa,
2080 event1.clone(),
2081 event0.clone(),
2082 ];
2083
2084 let room = make_room();
2086 let mut room_info = room.clone_info();
2087 cache_latest_events(&room, &mut room_info, events, None, None).await;
2088 room.set_room_info(room_info, RoomInfoNotableUpdateReasons::empty());
2089
2090 assert_eq!(
2092 ev_id(room.latest_event().map(|latest_event| latest_event.event().clone())),
2093 rawev_id(eventd)
2094 );
2095
2096 assert_eq!(
2098 rawevs_ids(&room.latest_encrypted_events),
2099 evs_ids(&[
2100 event9, event8, event7, event6, event5, event4, event3, event2, event1, event0
2101 ])
2102 );
2103 }
2104
2105 #[cfg(feature = "e2e-encryption")]
2106 #[async_test]
2107 async fn test_dont_overflow_capacity_if_previous_encrypted_events_exist() {
2108 let room = make_room();
2110 let mut room_info = room.clone_info();
2111 cache_latest_events(
2112 &room,
2113 &mut room_info,
2114 &[
2115 make_encrypted_event("$0"),
2116 make_encrypted_event("$1"),
2117 make_encrypted_event("$2"),
2118 make_encrypted_event("$3"),
2119 make_encrypted_event("$4"),
2120 make_encrypted_event("$5"),
2121 make_encrypted_event("$6"),
2122 make_encrypted_event("$7"),
2123 make_encrypted_event("$8"),
2124 make_encrypted_event("$9"),
2125 ],
2126 None,
2127 None,
2128 )
2129 .await;
2130 room.set_room_info(room_info, RoomInfoNotableUpdateReasons::empty());
2131
2132 assert_eq!(room.latest_encrypted_events.read().unwrap().len(), 10);
2134
2135 let eventa = make_encrypted_event("$a");
2137 let mut room_info = room.clone_info();
2138 cache_latest_events(&room, &mut room_info, &[eventa], None, None).await;
2139 room.set_room_info(room_info, RoomInfoNotableUpdateReasons::empty());
2140
2141 assert!(!rawevs_ids(&room.latest_encrypted_events).contains(&"$0".to_owned()));
2143
2144 assert_eq!(rawevs_ids(&room.latest_encrypted_events)[9], "$a");
2146 }
2147
2148 #[cfg(feature = "e2e-encryption")]
2149 #[async_test]
2150 async fn test_existing_encrypted_events_are_deleted_if_we_receive_unencrypted() {
2151 let room = make_room();
2153 let mut room_info = room.clone_info();
2154 cache_latest_events(
2155 &room,
2156 &mut room_info,
2157 &[make_encrypted_event("$0"), make_encrypted_event("$1"), make_encrypted_event("$2")],
2158 None,
2159 None,
2160 )
2161 .await;
2162 room.set_room_info(room_info.clone(), RoomInfoNotableUpdateReasons::empty());
2163
2164 let eventa = make_event("m.room.message", "$a");
2166 let eventb = make_encrypted_event("$b");
2167 cache_latest_events(&room, &mut room_info, &[eventa, eventb], None, None).await;
2168 room.set_room_info(room_info, RoomInfoNotableUpdateReasons::empty());
2169
2170 assert_eq!(rawevs_ids(&room.latest_encrypted_events), &["$b"]);
2172
2173 assert_eq!(rawev_id(room.latest_event().unwrap().event().clone()), "$a");
2175 }
2176
2177 #[async_test]
2178 async fn test_recency_stamp_is_found_when_processing_sliding_sync_response() {
2179 let client = logged_in_base_client(None).await;
2181 let room_id = room_id!("!r:e.uk");
2182
2183 let room = assign!(http::response::Room::new(), {
2185 bump_stamp: Some(42u32.into()),
2186 });
2187 let response = response_with_room(room_id, room);
2188 client
2189 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
2190 .await
2191 .expect("Failed to process sync");
2192
2193 let client_room = client.get_room(room_id).expect("No room found");
2195 assert_eq!(client_room.recency_stamp().expect("No recency stamp"), 42);
2196 }
2197
2198 #[async_test]
2199 async fn test_recency_stamp_can_be_overwritten_when_present_in_a_sliding_sync_response() {
2200 let client = logged_in_base_client(None).await;
2202 let room_id = room_id!("!r:e.uk");
2203
2204 {
2205 let room = assign!(http::response::Room::new(), {
2207 bump_stamp: Some(42u32.into()),
2208 });
2209 let response = response_with_room(room_id, room);
2210 client
2211 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
2212 .await
2213 .expect("Failed to process sync");
2214
2215 let client_room = client.get_room(room_id).expect("No room found");
2217 assert_eq!(client_room.recency_stamp().expect("No recency stamp"), 42);
2218 }
2219
2220 {
2221 let room = assign!(http::response::Room::new(), {
2223 bump_stamp: None,
2224 });
2225 let response = response_with_room(room_id, room);
2226 client
2227 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
2228 .await
2229 .expect("Failed to process sync");
2230
2231 let client_room = client.get_room(room_id).expect("No room found");
2233 assert_eq!(client_room.recency_stamp().expect("No recency stamp"), 42);
2234 }
2235
2236 {
2237 let room = assign!(http::response::Room::new(), {
2240 bump_stamp: Some(153u32.into()),
2241 });
2242 let response = response_with_room(room_id, room);
2243 client
2244 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
2245 .await
2246 .expect("Failed to process sync");
2247
2248 let client_room = client.get_room(room_id).expect("No room found");
2250 assert_eq!(client_room.recency_stamp().expect("No recency stamp"), 153);
2251 }
2252 }
2253
2254 #[async_test]
2255 async fn test_recency_stamp_can_trigger_a_notable_update_reason() {
2256 let client = logged_in_base_client(None).await;
2258 let mut room_info_notable_update_stream = client.room_info_notable_update_receiver();
2259 let room_id = room_id!("!r:e.uk");
2260
2261 let room = assign!(http::response::Room::new(), {
2263 bump_stamp: Some(42u32.into()),
2264 });
2265 let response = response_with_room(room_id, room);
2266 client
2267 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
2268 .await
2269 .expect("Failed to process sync");
2270
2271 assert_matches!(
2274 room_info_notable_update_stream.recv().await,
2275 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
2276 assert_eq!(received_room_id, room_id);
2277 assert!(!received_reasons.contains(RoomInfoNotableUpdateReasons::RECENCY_STAMP));
2278 }
2279 );
2280
2281 let room = assign!(http::response::Room::new(), {
2283 bump_stamp: Some(43u32.into()),
2284 });
2285 let response = response_with_room(room_id, room);
2286 client
2287 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
2288 .await
2289 .expect("Failed to process sync");
2290
2291 assert_matches!(
2293 room_info_notable_update_stream.recv().await,
2294 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
2295 assert_eq!(received_room_id, room_id);
2296 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::RECENCY_STAMP));
2297 }
2298 );
2299 }
2300
2301 #[async_test]
2302 async fn test_read_receipt_can_trigger_a_notable_update_reason() {
2303 let client = logged_in_base_client(None).await;
2305 let mut room_info_notable_update_stream = client.room_info_notable_update_receiver();
2306
2307 let room_id = room_id!("!r:e.uk");
2309 let room = http::response::Room::new();
2310 let response = response_with_room(room_id, room);
2311 client
2312 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
2313 .await
2314 .expect("Failed to process sync");
2315
2316 assert_matches!(
2318 room_info_notable_update_stream.recv().await,
2319 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
2320 assert_eq!(received_room_id, room_id);
2321 assert!(!received_reasons.contains(RoomInfoNotableUpdateReasons::READ_RECEIPT));
2322 }
2323 );
2324
2325 let room_id = room_id!("!r:e.uk");
2328 let events = vec![
2329 make_raw_event("m.room.message", "$3"),
2330 make_raw_event("m.room.message", "$4"),
2331 make_raw_event("m.read", "$5"),
2332 ];
2333 let room = assign!(http::response::Room::new(), {
2334 timeline: events,
2335 });
2336 let response = response_with_room(room_id, room);
2337 client
2338 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
2339 .await
2340 .expect("Failed to process sync");
2341
2342 assert_matches!(
2344 room_info_notable_update_stream.recv().await,
2345 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
2346 assert_eq!(received_room_id, room_id);
2347 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::READ_RECEIPT));
2348 }
2349 );
2350 }
2351
2352 #[async_test]
2353 async fn test_leaving_room_can_trigger_a_notable_update_reason() {
2354 let client = logged_in_base_client(None).await;
2356 let mut room_info_notable_update_stream = client.room_info_notable_update_receiver();
2357
2358 let room_id = room_id!("!r:e.uk");
2360 let room = http::response::Room::new();
2361 let response = response_with_room(room_id, room);
2362 client
2363 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
2364 .await
2365 .expect("Failed to process sync");
2366
2367 let _ = room_info_notable_update_stream.recv().await;
2369
2370 let room_id = room_id!("!r:e.uk");
2372 let events = vec![Raw::from_json_string(
2373 json!({
2374 "type": "m.room.member",
2375 "event_id": "$3",
2376 "content": { "membership": "join" },
2377 "sender": "@u:h.uk",
2378 "origin_server_ts": 12344445,
2379 "state_key": "@u:e.uk",
2380 })
2381 .to_string(),
2382 )
2383 .unwrap()];
2384 let room = assign!(http::response::Room::new(), {
2385 required_state: events,
2386 });
2387 let response = response_with_room(room_id, room);
2388 client
2389 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
2390 .await
2391 .expect("Failed to process sync");
2392
2393 assert_matches!(
2395 room_info_notable_update_stream.recv().await,
2396 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
2397 assert_eq!(received_room_id, room_id);
2398 assert!(!received_reasons.contains(RoomInfoNotableUpdateReasons::MEMBERSHIP));
2399 }
2400 );
2401
2402 let events = vec![Raw::from_json_string(
2403 json!({
2404 "type": "m.room.member",
2405 "event_id": "$3",
2406 "content": { "membership": "leave" },
2407 "sender": "@u:h.uk",
2408 "origin_server_ts": 12344445,
2409 "state_key": "@u:e.uk",
2410 })
2411 .to_string(),
2412 )
2413 .unwrap()];
2414 let room = assign!(http::response::Room::new(), {
2415 required_state: events,
2416 });
2417 let response = response_with_room(room_id, room);
2418 client
2419 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
2420 .await
2421 .expect("Failed to process sync");
2422
2423 let update = room_info_notable_update_stream.recv().await;
2425 assert_matches!(
2426 update,
2427 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
2428 assert_eq!(received_room_id, room_id);
2429 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::MEMBERSHIP));
2430 }
2431 );
2432 }
2433
2434 #[async_test]
2435 async fn test_unread_marker_can_trigger_a_notable_update_reason() {
2436 let client = logged_in_base_client(None).await;
2438 let mut room_info_notable_update_stream = client.room_info_notable_update_receiver();
2439
2440 let room_id = room_id!("!r:e.uk");
2442 let room = http::response::Room::new();
2443 let response = response_with_room(room_id, room);
2444 client
2445 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
2446 .await
2447 .expect("Failed to process sync");
2448
2449 assert_matches!(
2451 room_info_notable_update_stream.recv().await,
2452 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
2453 assert_eq!(received_room_id, room_id);
2454 assert!(!received_reasons.contains(RoomInfoNotableUpdateReasons::UNREAD_MARKER));
2455 }
2456 );
2457
2458 let room_id = room_id!("!r:e.uk");
2461 let room_account_data_events = vec![Raw::from_json_string(
2462 json!({
2463 "type": "com.famedly.marked_unread",
2464 "event_id": "$1",
2465 "content": { "unread": true },
2466 "sender": client.session_meta().unwrap().user_id,
2467 "origin_server_ts": 12344445,
2468 })
2469 .to_string(),
2470 )
2471 .unwrap()];
2472 let mut response = response_with_room(room_id, http::response::Room::new());
2473 response.extensions.account_data.rooms.insert(room_id.to_owned(), room_account_data_events);
2474
2475 client
2476 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
2477 .await
2478 .expect("Failed to process sync");
2479
2480 assert_matches!(
2482 room_info_notable_update_stream.recv().await,
2483 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
2484 assert_eq!(received_room_id, room_id);
2485 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::UNREAD_MARKER), "{received_reasons:?}");
2486 }
2487 );
2488
2489 client
2491 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
2492 .await
2493 .expect("Failed to process sync");
2494
2495 assert_matches!(
2496 room_info_notable_update_stream.recv().await,
2497 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
2498 assert_eq!(received_room_id, room_id);
2499 assert!(!received_reasons.contains(RoomInfoNotableUpdateReasons::UNREAD_MARKER));
2500 }
2501 );
2502
2503 let room_account_data_events = vec![Raw::from_json_string(
2505 json!({
2506 "type": "com.famedly.marked_unread",
2507 "event_id": "$1",
2508 "content": { "unread": false },
2509 "sender": client.session_meta().unwrap().user_id,
2510 "origin_server_ts": 12344445,
2511 })
2512 .to_string(),
2513 )
2514 .unwrap()];
2515 response.extensions.account_data.rooms.insert(room_id.to_owned(), room_account_data_events);
2516 client
2517 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
2518 .await
2519 .expect("Failed to process sync");
2520
2521 assert_matches!(
2522 room_info_notable_update_stream.recv().await,
2523 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
2524 assert_eq!(received_room_id, room_id);
2525 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::UNREAD_MARKER));
2526 }
2527 );
2528 }
2529
2530 #[async_test]
2531 async fn test_pinned_events_are_updated_on_sync() {
2532 let user_a_id = user_id!("@a:e.uk");
2533 let client = logged_in_base_client(Some(user_a_id)).await;
2534 let room_id = room_id!("!r:e.uk");
2535 let pinned_event_id = owned_event_id!("$an-id:e.uk");
2536
2537 let mut room_response = http::response::Room::new();
2539 set_room_joined(&mut room_response, user_a_id);
2540 let response = response_with_room(room_id, room_response);
2541 client
2542 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
2543 .await
2544 .expect("Failed to process sync");
2545
2546 let room = client.get_room(room_id).unwrap();
2548 let pinned_event_ids = room.pinned_event_ids();
2549 assert_matches!(pinned_event_ids, None);
2550
2551 let mut room_response = http::response::Room::new();
2553 room_response.required_state.push(make_state_event(
2554 user_a_id,
2555 "",
2556 RoomPinnedEventsEventContent::new(vec![pinned_event_id.clone()]),
2557 None,
2558 ));
2559 let response = response_with_room(room_id, room_response);
2560 client
2561 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
2562 .await
2563 .expect("Failed to process sync");
2564
2565 let pinned_event_ids = room.pinned_event_ids().unwrap_or_default();
2566 assert_eq!(pinned_event_ids.len(), 1);
2567 assert_eq!(pinned_event_ids[0], pinned_event_id);
2568
2569 let mut room_response = http::response::Room::new();
2571 room_response.required_state.push(make_state_event(
2572 user_a_id,
2573 "",
2574 RoomPinnedEventsEventContent::new(Vec::new()),
2575 None,
2576 ));
2577 let response = response_with_room(room_id, room_response);
2578 client
2579 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
2580 .await
2581 .expect("Failed to process sync");
2582 let pinned_event_ids = room.pinned_event_ids().unwrap();
2583 assert!(pinned_event_ids.is_empty());
2584 }
2585
2586 #[async_test]
2587 async fn test_dms_are_processed_in_any_sync_response() {
2588 let current_user_id = user_id!("@current:e.uk");
2589 let client = logged_in_base_client(Some(current_user_id)).await;
2590 let user_a_id = user_id!("@a:e.uk");
2591 let user_b_id = user_id!("@b:e.uk");
2592 let room_id_1 = room_id!("!r:e.uk");
2593 let room_id_2 = room_id!("!s:e.uk");
2594
2595 let mut room_response = http::response::Room::new();
2596 set_room_joined(&mut room_response, user_a_id);
2597 let mut response = response_with_room(room_id_1, room_response);
2598 let mut direct_content: BTreeMap<OwnedDirectUserIdentifier, Vec<OwnedRoomId>> =
2599 BTreeMap::new();
2600 direct_content.insert(user_a_id.into(), vec![room_id_1.to_owned()]);
2601 direct_content.insert(user_b_id.into(), vec![room_id_2.to_owned()]);
2602 response
2603 .extensions
2604 .account_data
2605 .global
2606 .push(make_global_account_data_event(DirectEventContent(direct_content)));
2607 client
2608 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
2609 .await
2610 .expect("Failed to process sync");
2611
2612 let room_1 = client.get_room(room_id_1).unwrap();
2613 assert!(room_1.is_direct().await.unwrap());
2614
2615 let mut room_response = http::response::Room::new();
2617 set_room_joined(&mut room_response, user_b_id);
2618 let response = response_with_room(room_id_2, room_response);
2619 client
2620 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
2621 .await
2622 .expect("Failed to process sync");
2623
2624 let room_2 = client.get_room(room_id_2).unwrap();
2625 assert!(room_2.is_direct().await.unwrap());
2626 }
2627
2628 #[async_test]
2629 async fn test_room_encryption_state_is_and_is_not_encrypted() {
2630 let user_id = user_id!("@raclette:patate");
2631 let client = logged_in_base_client(Some(user_id)).await;
2632 let room_id_0 = room_id!("!r0");
2633 let room_id_1 = room_id!("!r1");
2634 let room_id_2 = room_id!("!r2");
2635
2636 let requested_required_states = RequestedRequiredStates::from(&{
2653 let mut request = http::Request::new();
2654
2655 request.room_subscriptions.insert(room_id_0.to_owned(), {
2656 let mut room_subscription = http::request::RoomSubscription::default();
2657
2658 room_subscription
2659 .required_state
2660 .push((StateEventType::RoomEncryption, "".to_owned()));
2661
2662 room_subscription
2663 });
2664
2665 request
2666 });
2667
2668 let mut response = http::Response::new("0".to_owned());
2669
2670 {
2674 let not_encrypted_room = http::response::Room::new();
2675 let mut encrypted_room = http::response::Room::new();
2676 set_room_is_encrypted(&mut encrypted_room, user_id);
2677
2678 response.rooms.insert(room_id_0.to_owned(), encrypted_room.clone());
2679 response.rooms.insert(room_id_1.to_owned(), encrypted_room);
2680 response.rooms.insert(room_id_2.to_owned(), not_encrypted_room);
2681 }
2682
2683 client
2684 .process_sliding_sync(&response, &(), &requested_required_states)
2685 .await
2686 .expect("Failed to process sync");
2687
2688 assert_matches!(
2690 client.get_room(room_id_0).unwrap().encryption_state(),
2691 EncryptionState::Encrypted
2692 );
2693 assert_matches!(
2694 client.get_room(room_id_1).unwrap().encryption_state(),
2695 EncryptionState::Encrypted
2696 );
2697 assert_matches!(
2699 client.get_room(room_id_2).unwrap().encryption_state(),
2700 EncryptionState::NotEncrypted
2701 )
2702 }
2703
2704 #[async_test]
2705 async fn test_room_encryption_state_is_unknown() {
2706 let user_id = user_id!("@raclette:patate");
2707 let client = logged_in_base_client(Some(user_id)).await;
2708 let room_id_0 = room_id!("!r0");
2709 let room_id_1 = room_id!("!r1");
2710
2711 let requested_required_states = RequestedRequiredStates::from(&http::Request::new());
2724
2725 let mut response = http::Response::new("0".to_owned());
2726
2727 {
2729 let not_encrypted_room = http::response::Room::new();
2730 let mut encrypted_room = http::response::Room::new();
2731 set_room_is_encrypted(&mut encrypted_room, user_id);
2732
2733 response.rooms.insert(room_id_0.to_owned(), encrypted_room);
2734 response.rooms.insert(room_id_1.to_owned(), not_encrypted_room);
2735 }
2736
2737 client
2738 .process_sliding_sync(&response, &(), &requested_required_states)
2739 .await
2740 .expect("Failed to process sync");
2741
2742 assert_matches!(
2745 client.get_room(room_id_0).unwrap().encryption_state(),
2746 EncryptionState::Encrypted
2747 );
2748 assert_matches!(
2751 client.get_room(room_id_1).unwrap().encryption_state(),
2752 EncryptionState::Unknown
2753 );
2754 }
2755
2756 #[cfg(feature = "e2e-encryption")]
2757 async fn choose_event_to_cache(events: &[TimelineEvent]) -> Option<TimelineEvent> {
2758 let room = make_room();
2759 let mut room_info = room.clone_info();
2760 cache_latest_events(&room, &mut room_info, events, None, None).await;
2761 room.set_room_info(room_info, RoomInfoNotableUpdateReasons::empty());
2762 room.latest_event().map(|latest_event| latest_event.event().clone())
2763 }
2764
2765 #[cfg(feature = "e2e-encryption")]
2766 fn rawev_id(event: TimelineEvent) -> String {
2767 event.event_id().unwrap().to_string()
2768 }
2769
2770 fn ev_id(event: Option<TimelineEvent>) -> String {
2771 event.unwrap().event_id().unwrap().to_string()
2772 }
2773
2774 #[cfg(feature = "e2e-encryption")]
2775 fn rawevs_ids(events: &Arc<SyncRwLock<RingBuffer<Raw<AnySyncTimelineEvent>>>>) -> Vec<String> {
2776 events.read().unwrap().iter().map(|e| e.get_field("event_id").unwrap().unwrap()).collect()
2777 }
2778
2779 #[cfg(feature = "e2e-encryption")]
2780 fn evs_ids(events: &[TimelineEvent]) -> Vec<String> {
2781 events.iter().map(|e| e.event_id().unwrap().to_string()).collect()
2782 }
2783
2784 #[cfg(feature = "e2e-encryption")]
2785 fn make_room() -> Room {
2786 let (sender, _receiver) = tokio::sync::broadcast::channel(1);
2787
2788 Room::new(
2789 user_id!("@u:e.co"),
2790 Arc::new(MemoryStore::new()),
2791 room_id!("!r:e.co"),
2792 RoomState::Joined,
2793 sender,
2794 )
2795 }
2796
2797 fn make_raw_event(event_type: &str, id: &str) -> Raw<AnySyncTimelineEvent> {
2798 Raw::from_json_string(
2799 json!({
2800 "type": event_type,
2801 "event_id": id,
2802 "content": { "msgtype": "m.text", "body": "my msg" },
2803 "sender": "@u:h.uk",
2804 "origin_server_ts": 12344445,
2805 })
2806 .to_string(),
2807 )
2808 .unwrap()
2809 }
2810
2811 #[cfg(feature = "e2e-encryption")]
2812 fn make_event(event_type: &str, id: &str) -> TimelineEvent {
2813 TimelineEvent::new(make_raw_event(event_type, id))
2814 }
2815
2816 #[cfg(feature = "e2e-encryption")]
2817 fn make_encrypted_event(id: &str) -> TimelineEvent {
2818 TimelineEvent::new_utd_event(
2819 Raw::from_json_string(
2820 json!({
2821 "type": "m.room.encrypted",
2822 "event_id": id,
2823 "content": {
2824 "algorithm": "m.megolm.v1.aes-sha2",
2825 "ciphertext": "",
2826 "sender_key": "",
2827 "device_id": "",
2828 "session_id": "",
2829 },
2830 "sender": "@u:h.uk",
2831 "origin_server_ts": 12344445,
2832 })
2833 .to_string(),
2834 )
2835 .unwrap(),
2836 UnableToDecryptInfo {
2837 session_id: Some("".to_owned()),
2838 reason: UnableToDecryptReason::MissingMegolmSession { withheld_code: None },
2839 },
2840 )
2841 }
2842
2843 async fn membership(
2844 client: &BaseClient,
2845 room_id: &RoomId,
2846 user_id: &UserId,
2847 ) -> MembershipState {
2848 let room = client.get_room(room_id).expect("Room not found!");
2849 let member = room.get_member(user_id).await.unwrap().expect("B not in room");
2850 member.membership().clone()
2851 }
2852
2853 fn direct_targets(client: &BaseClient, room_id: &RoomId) -> HashSet<OwnedDirectUserIdentifier> {
2854 let room = client.get_room(room_id).expect("Room not found!");
2855 room.direct_targets()
2856 }
2857
2858 async fn create_dm(
2861 client: &BaseClient,
2862 room_id: &RoomId,
2863 my_id: &UserId,
2864 their_id: &UserId,
2865 other_state: MembershipState,
2866 ) {
2867 let mut room = http::response::Room::new();
2868 set_room_joined(&mut room, my_id);
2869
2870 match other_state {
2871 MembershipState::Join => {
2872 room.joined_count = Some(uint!(2));
2873 room.invited_count = None;
2874 }
2875
2876 MembershipState::Invite => {
2877 room.joined_count = Some(uint!(1));
2878 room.invited_count = Some(uint!(1));
2879 }
2880
2881 _ => {
2882 room.joined_count = Some(uint!(1));
2883 room.invited_count = None;
2884 }
2885 }
2886
2887 room.required_state.push(make_membership_event(their_id, other_state));
2888
2889 let mut response = response_with_room(room_id, room);
2890 set_direct_with(&mut response, their_id.to_owned(), vec![room_id.to_owned()]);
2891 client
2892 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
2893 .await
2894 .expect("Failed to process sync");
2895 }
2896
2897 async fn update_room_membership(
2899 client: &BaseClient,
2900 room_id: &RoomId,
2901 user_id: &UserId,
2902 new_state: MembershipState,
2903 ) {
2904 let mut room = http::response::Room::new();
2905 room.required_state.push(make_membership_event(user_id, new_state));
2906 let response = response_with_room(room_id, room);
2907 client
2908 .process_sliding_sync(&response, &(), &RequestedRequiredStates::default())
2909 .await
2910 .expect("Failed to process sync");
2911 }
2912
2913 fn set_direct_with(
2914 response: &mut http::Response,
2915 user_id: OwnedUserId,
2916 room_ids: Vec<OwnedRoomId>,
2917 ) {
2918 let mut direct_content: BTreeMap<OwnedDirectUserIdentifier, Vec<OwnedRoomId>> =
2919 BTreeMap::new();
2920 direct_content.insert(user_id.into(), room_ids);
2921 response
2922 .extensions
2923 .account_data
2924 .global
2925 .push(make_global_account_data_event(DirectEventContent(direct_content)));
2926 }
2927
2928 fn response_with_room(room_id: &RoomId, room: http::response::Room) -> http::Response {
2929 let mut response = http::Response::new("5".to_owned());
2930 response.rooms.insert(room_id.to_owned(), room);
2931 response
2932 }
2933
2934 fn room_with_avatar(avatar_uri: &MxcUri, user_id: &UserId) -> http::response::Room {
2935 let mut room = http::response::Room::new();
2936
2937 let mut avatar_event_content = RoomAvatarEventContent::new();
2938 avatar_event_content.url = Some(avatar_uri.to_owned());
2939
2940 room.required_state.push(make_state_event(user_id, "", avatar_event_content, None));
2941
2942 room
2943 }
2944
2945 fn room_with_canonical_alias(
2946 room_alias_id: &RoomAliasId,
2947 user_id: &UserId,
2948 ) -> http::response::Room {
2949 let mut room = http::response::Room::new();
2950
2951 let mut canonical_alias_event_content = RoomCanonicalAliasEventContent::new();
2952 canonical_alias_event_content.alias = Some(room_alias_id.to_owned());
2953
2954 room.required_state.push(make_state_event(
2955 user_id,
2956 "",
2957 canonical_alias_event_content,
2958 None,
2959 ));
2960
2961 room
2962 }
2963
2964 fn room_with_timeline(events: &[serde_json::Value]) -> http::response::Room {
2965 let mut room = http::response::Room::new();
2966 room.timeline.extend(
2967 events
2968 .iter()
2969 .map(|e| Raw::from_json_string(e.to_string()).unwrap())
2970 .collect::<Vec<_>>(),
2971 );
2972 room
2973 }
2974
2975 fn set_room_name(room: &mut http::response::Room, sender: &UserId, name: String) {
2976 room.required_state.push(make_state_event(
2977 sender,
2978 "",
2979 RoomNameEventContent::new(name),
2980 None,
2981 ));
2982 }
2983
2984 fn set_room_invited(room: &mut http::response::Room, inviter: &UserId, invitee: &UserId) {
2985 let evt = Raw::new(&json!({
2989 "type": "m.room.member",
2990 "sender": inviter,
2991 "content": {
2992 "is_direct": true,
2993 "membership": "invite",
2994 },
2995 "state_key": invitee,
2996 }))
2997 .expect("Failed to make raw event")
2998 .cast();
2999
3000 room.invite_state = Some(vec![evt]);
3001
3002 room.required_state.push(make_state_event(
3005 inviter,
3006 invitee.as_str(),
3007 RoomMemberEventContent::new(MembershipState::Invite),
3008 None,
3009 ));
3010 }
3011
3012 fn set_room_knocked(room: &mut http::response::Room, knocker: &UserId) {
3013 let evt = Raw::new(&json!({
3017 "type": "m.room.member",
3018 "sender": knocker,
3019 "content": {
3020 "is_direct": true,
3021 "membership": "knock",
3022 },
3023 "state_key": knocker,
3024 }))
3025 .expect("Failed to make raw event")
3026 .cast();
3027
3028 room.invite_state = Some(vec![evt]);
3029 }
3030
3031 fn set_room_joined(room: &mut http::response::Room, user_id: &UserId) {
3032 room.required_state.push(make_membership_event(user_id, MembershipState::Join));
3033 }
3034
3035 fn set_room_left(room: &mut http::response::Room, user_id: &UserId) {
3036 room.required_state.push(make_membership_event(user_id, MembershipState::Leave));
3037 }
3038
3039 fn set_room_left_as_timeline_event(room: &mut http::response::Room, user_id: &UserId) {
3040 room.timeline.push(make_membership_event(user_id, MembershipState::Leave));
3041 }
3042
3043 fn set_room_is_encrypted(room: &mut http::response::Room, user_id: &UserId) {
3044 room.required_state.push(make_encryption_event(user_id));
3045 }
3046
3047 fn make_membership_event<K>(user_id: &UserId, state: MembershipState) -> Raw<K> {
3048 make_state_event(user_id, user_id.as_str(), RoomMemberEventContent::new(state), None)
3049 }
3050
3051 fn make_encryption_event<K>(user_id: &UserId) -> Raw<K> {
3052 make_state_event(user_id, "", RoomEncryptionEventContent::with_recommended_defaults(), None)
3053 }
3054
3055 fn make_global_account_data_event<C: GlobalAccountDataEventContent, E>(content: C) -> Raw<E> {
3056 Raw::new(&json!({
3057 "type": content.event_type(),
3058 "content": content,
3059 }))
3060 .expect("Failed to create account data event")
3061 .cast()
3062 }
3063
3064 fn make_state_event<C: StateEventContent, E>(
3065 sender: &UserId,
3066 state_key: &str,
3067 content: C,
3068 prev_content: Option<C>,
3069 ) -> Raw<E> {
3070 let unsigned = if let Some(prev_content) = prev_content {
3071 json!({ "prev_content": prev_content })
3072 } else {
3073 json!({})
3074 };
3075
3076 Raw::new(&json!({
3077 "type": content.event_type(),
3078 "state_key": state_key,
3079 "content": content,
3080 "event_id": event_id!("$evt"),
3081 "sender": sender,
3082 "origin_server_ts": 10,
3083 "unsigned": unsigned,
3084 }))
3085 .expect("Failed to create state event")
3086 .cast()
3087 }
3088}