1use std::collections::BTreeSet;
16
17use as_variant::as_variant;
18use ruma::{
19 RoomId,
20 events::{AnySyncStateEvent, SyncStateEvent},
21 serde::Raw,
22};
23use tracing::error;
24
25use super::Context;
26#[cfg(feature = "experimental-encrypted-state-events")]
27use super::e2ee;
28use crate::{store::BaseStateStore, utils::RawStateEventWithKeys};
29
30pub mod sync {
32 use std::collections::BTreeSet;
33
34 use as_variant::as_variant;
35 use ruma::{
36 OwnedUserId, RoomId, UserId,
37 events::{
38 AnySyncStateEvent, AnySyncTimelineEvent, StateEventType, room::member::MembershipState,
39 },
40 };
41 use tracing::instrument;
42
43 use super::{super::profiles, Context, Raw};
44 #[cfg(feature = "experimental-encrypted-state-events")]
45 use crate::response_processors::e2ee;
46 use crate::{
47 RoomInfo, RoomInfoNotableUpdateReasons, RoomState,
48 store::{
49 AvatarCache, BaseStateStore, Result as StoreResult, ambiguity_map::AmbiguityCache,
50 },
51 sync::State,
52 utils::RawStateEventWithKeys,
53 };
54
55 impl State {
56 pub(crate) fn collect(
61 &self,
62 timeline: &[Raw<AnySyncTimelineEvent>],
63 ) -> Vec<RawStateEventWithKeys<AnySyncStateEvent>> {
64 match self {
65 Self::Before(events) => events
66 .iter()
67 .cloned()
68 .filter_map(RawStateEventWithKeys::try_from_raw_state_event)
69 .chain(
70 timeline
71 .iter()
72 .filter_map(RawStateEventWithKeys::try_from_raw_timeline_event),
73 )
74 .collect(),
75 Self::After(events) => events
76 .iter()
77 .cloned()
78 .filter_map(RawStateEventWithKeys::try_from_raw_state_event)
79 .collect(),
80 }
81 }
82 }
83
84 #[instrument(skip_all, fields(room_id = ?room_info.room_id))]
94 pub async fn dispatch<U>(
95 context: &mut Context,
96 raw_events: Vec<RawStateEventWithKeys<AnySyncStateEvent>>,
97 room_info: &mut RoomInfo,
98 ambiguity_cache: &mut AmbiguityCache,
99 avatar_cache: &mut AvatarCache,
100 new_users: &mut U,
101 state_store: &BaseStateStore,
102 #[cfg(feature = "experimental-encrypted-state-events")] e2ee: &e2ee::E2EE<'_>,
103 ) -> StoreResult<()>
104 where
105 U: NewUsers,
106 {
107 for mut raw_event in raw_events {
108 match (&raw_event.event_type, raw_event.state_key.as_str()) {
109 (StateEventType::RoomMember, _) => {
110 room_info.handle_state_event(&mut raw_event);
111
112 dispatch_room_member(
113 context,
114 &room_info.room_id,
115 &mut raw_event,
116 ambiguity_cache,
117 avatar_cache,
118 new_users,
119 )
120 .await?;
121 }
122
123 (StateEventType::RoomCreate, "") => {
124 super::validate_create_event_predecessor(
125 context,
126 &room_info.room_id,
127 &mut raw_event,
128 state_store,
129 );
130
131 room_info.handle_state_event(&mut raw_event);
132 }
133
134 (StateEventType::RoomTombstone, "") => {
135 if super::is_tombstone_event_valid(
136 context,
137 &room_info.room_id,
138 &mut raw_event,
139 state_store,
140 ) {
141 room_info.handle_state_event(&mut raw_event);
142 } else {
143 continue;
146 }
147 }
148
149 #[cfg(feature = "experimental-encrypted-state-events")]
150 (StateEventType::RoomEncrypted, _) => {
151 let Some(mut raw_event) =
152 super::decrypt_state_event(&mut raw_event, &room_info.room_id, e2ee).await
153 else {
154 continue;
155 };
156
157 room_info.handle_state_event(&mut raw_event);
158 }
159
160 _ => {
161 room_info.handle_state_event(&mut raw_event);
162 }
163 }
164
165 context
166 .state_changes
167 .state
168 .entry(room_info.room_id.to_owned())
169 .or_default()
170 .entry(raw_event.event_type)
171 .or_default()
172 .insert(raw_event.state_key, raw_event.raw);
173 }
174
175 Ok(())
176 }
177
178 async fn dispatch_room_member<U>(
180 context: &mut Context,
181 room_id: &RoomId,
182 raw_event: &mut RawStateEventWithKeys<AnySyncStateEvent>,
183 ambiguity_cache: &mut AmbiguityCache,
184 avatar_cache: &mut AvatarCache,
185 new_users: &mut U,
186 ) -> StoreResult<()>
187 where
188 U: NewUsers,
189 {
190 let Some(event) = raw_event
191 .deserialize_as(|any_event| as_variant!(any_event, AnySyncStateEvent::RoomMember))
192 else {
193 return Ok(());
194 };
195
196 ambiguity_cache.handle_event(&context.state_changes, room_id, event).await?;
197 avatar_cache.handle_event(&context.state_changes, room_id, event).await?;
198
199 match event.membership() {
200 MembershipState::Join | MembershipState::Invite => {
201 new_users.insert(event.state_key());
202 }
203 _ => (),
204 }
205
206 profiles::upsert_or_delete(context, room_id, event);
207
208 Ok(())
209 }
210
211 pub(crate) trait NewUsers {
213 fn insert(&mut self, user_id: &UserId);
215 }
216
217 impl NewUsers for BTreeSet<OwnedUserId> {
218 fn insert(&mut self, user_id: &UserId) {
219 self.insert(user_id.to_owned());
220 }
221 }
222
223 impl NewUsers for () {
224 fn insert(&mut self, _user_id: &UserId) {}
225 }
226
227 pub fn own_membership_and_update_room_state(
231 context: &mut Context,
232 user_id: &UserId,
233 state_events: &mut [RawStateEventWithKeys<AnySyncStateEvent>],
234 room_info: &mut RoomInfo,
235 ) {
236 if let Some(member) = state_events.iter_mut().rev().find_map(|event| {
240 if event.event_type == StateEventType::RoomMember
242 && event.state_key.as_str() == user_id
243 && let Some(member) = event.deserialize_as(|any_event| {
244 as_variant!(any_event, AnySyncStateEvent::RoomMember)
245 })
246 {
247 Some(member)
248 } else {
249 None
250 }
251 }) {
252 let new_state: RoomState = member.membership().into();
253
254 if new_state != room_info.state() {
255 room_info.set_state(new_state);
256
257 context
259 .room_info_notable_updates
260 .entry(room_info.room_id.to_owned())
261 .or_default()
262 .insert(RoomInfoNotableUpdateReasons::MEMBERSHIP);
263 }
264 }
265 }
266}
267
268pub mod stripped {
270 use std::collections::BTreeMap;
271
272 use ruma::{
273 RoomId, UserId,
274 events::{AnyStrippedStateEvent, StateEventType},
275 push::Action,
276 };
277 use tracing::instrument;
278
279 use super::{
280 super::{notification, timeline},
281 Context, Raw,
282 };
283 use crate::{RawStateEventWithKeys, Result, Room, RoomInfo, RoomInfoNotableUpdateReasons};
284
285 pub fn collect(
288 raw_events: &[Raw<AnyStrippedStateEvent>],
289 ) -> Vec<RawStateEventWithKeys<AnyStrippedStateEvent>> {
290 raw_events
291 .iter()
292 .cloned()
293 .filter_map(RawStateEventWithKeys::try_from_raw_state_event)
294 .collect()
295 }
296
297 #[instrument(skip_all, fields(room_id = ?room_info.room_id))]
314 pub(crate) async fn dispatch_invite_or_knock(
315 context: &mut Context,
316 raw_events: Vec<RawStateEventWithKeys<AnyStrippedStateEvent>>,
317 room: &Room,
318 room_info: &mut RoomInfo,
319 user_id: &UserId,
320 mut notification: notification::Notification<'_>,
321 ) -> Result<()> {
322 let mut state_events = BTreeMap::new();
323
324 own_membership(context, room.room_id(), user_id, &raw_events);
325
326 for mut raw_event in raw_events {
327 room_info.handle_stripped_state_event(&mut raw_event);
328
329 state_events
330 .entry(raw_event.event_type)
331 .or_insert_with(BTreeMap::new)
332 .insert(raw_event.state_key, raw_event.raw);
333 }
334
335 context
336 .state_changes
337 .stripped_state
338 .insert(room_info.room_id().to_owned(), state_events.clone());
339
340 if let Some(push_condition_room_ctx) =
343 timeline::get_push_room_context(context, room, room_info).await?
344 {
345 for event in state_events.values().flat_map(|map| map.values()) {
347 notification
348 .push_notification_from_event_if(
349 &push_condition_room_ctx,
350 event,
351 Action::should_notify,
352 )
353 .await;
354 }
355 }
356
357 Ok(())
358 }
359
360 pub fn own_membership(
363 context: &mut Context,
364 room_id: &RoomId,
365 user_id: &UserId,
366 raw_state_events: &[RawStateEventWithKeys<AnyStrippedStateEvent>],
367 ) {
368 if raw_state_events.iter().rev().any(|raw_event| {
372 raw_event.event_type == StateEventType::RoomMember
374 && raw_event.state_key == user_id.as_str()
375 }) {
376 context
378 .room_info_notable_updates
379 .entry(room_id.to_owned())
380 .or_default()
381 .insert(RoomInfoNotableUpdateReasons::MEMBERSHIP);
382 }
383 }
384}
385
386pub fn validate_create_event_predecessor(
391 context: &mut Context,
392 room_id: &RoomId,
393 raw_event: &mut RawStateEventWithKeys<AnySyncStateEvent>,
394 state_store: &BaseStateStore,
395) {
396 let mut already_seen = BTreeSet::new();
397 already_seen.insert(room_id.to_owned());
398
399 let Some(event) =
400 raw_event.deserialize_as(|any_event| as_variant!(any_event, AnySyncStateEvent::RoomCreate))
401 else {
402 return;
403 };
404
405 let content = match event {
407 SyncStateEvent::Original(event) => &event.content,
408 SyncStateEvent::Redacted(event) => &event.content,
409 };
410
411 let Some(mut predecessor_room_id) =
412 content.predecessor.as_ref().map(|predecessor| predecessor.room_id.clone())
413 else {
414 return;
416 };
417
418 loop {
419 if already_seen.contains(&predecessor_room_id) {
423 let mut event = event.clone();
426
427 match &mut event {
428 SyncStateEvent::Original(event) => event.content.predecessor.take(),
429 SyncStateEvent::Redacted(event) => event.content.predecessor.take(),
430 };
431
432 raw_event.set_cached_event(event.into());
433
434 return;
435 }
436
437 already_seen.insert(predecessor_room_id.clone());
438
439 let Some(next_predecessor_room_id) = context
442 .state_changes
443 .room_infos
444 .get(&predecessor_room_id)
445 .and_then(|room_info| Some(room_info.create()?.predecessor.as_ref()?.room_id.clone()))
446 .or_else(|| {
447 state_store
448 .room(&predecessor_room_id)
449 .and_then(|room| Some(room.predecessor_room()?.room_id))
450 })
451 else {
452 break;
454 };
455
456 predecessor_room_id = next_predecessor_room_id;
457 }
458}
459
460pub fn is_tombstone_event_valid(
462 context: &mut Context,
463 room_id: &RoomId,
464 raw_event: &mut RawStateEventWithKeys<AnySyncStateEvent>,
465 state_store: &BaseStateStore,
466) -> bool {
467 let mut already_seen = BTreeSet::new();
468 already_seen.insert(room_id.to_owned());
469
470 let Some(tombstone) = raw_event
471 .deserialize_as(|any_event| as_variant!(any_event, AnySyncStateEvent::RoomTombstone))
472 .and_then(|event| Some(&event.as_original()?.content))
473 else {
474 return true;
476 };
477
478 let mut successor_room_id = tombstone.replacement_room.clone();
479
480 loop {
481 if already_seen.contains(AsRef::<RoomId>::as_ref(&successor_room_id)) {
484 error!(?room_id, ?tombstone, "`m.room.tombstone` event is invalid, it creates a loop");
486 return false;
487 }
488
489 already_seen.insert(successor_room_id.clone());
490
491 let Some(next_successor_room_id) = context
493 .state_changes
494 .room_infos
495 .get(&successor_room_id)
496 .and_then(|room_info| room_info.tombstone()?.replacement_room.clone())
497 .or_else(|| {
498 state_store
499 .room(&successor_room_id)
500 .and_then(|room| Some(room.successor_room()?.room_id))
501 })
502 else {
503 break;
505 };
506
507 successor_room_id = next_successor_room_id;
508 }
509
510 true
511}
512
513#[cfg(feature = "experimental-encrypted-state-events")]
518async fn decrypt_state_event(
519 raw_event: &mut RawStateEventWithKeys<AnySyncStateEvent>,
520 room_id: &RoomId,
521 e2ee: &e2ee::E2EE<'_>,
522) -> Option<RawStateEventWithKeys<AnySyncStateEvent>> {
523 use matrix_sdk_crypto::RoomEventDecryptionResult;
524 use ruma::OwnedEventId;
525 use tracing::{trace, warn};
526
527 let event_id = match raw_event.raw.get_field::<OwnedEventId>("event_id") {
528 Ok(Some(event_id)) => event_id,
529 Ok(None) => {
530 warn!("Couldn't deserialize encrypted state event's ID: missing `event_id` field");
531 return None;
532 }
533 Err(error) => {
534 warn!(?error, "Couldn't deserialize encrypted state event's ID");
535 return None;
536 }
537 };
538
539 trace!(?event_id, "Received encrypted state event, attempting decryption...");
540
541 let olm_machine = e2ee.olm_machine?;
542
543 let decrypted_event = olm_machine
544 .try_decrypt_room_event(
545 raw_event.raw.cast_ref_unchecked(),
546 room_id,
547 e2ee.decryption_settings,
548 )
549 .await
550 .expect("OlmMachine was not started");
551
552 let RoomEventDecryptionResult::Decrypted(decrypted_event) = decrypted_event else {
554 warn!(?event_id, "Failed to decrypt state event");
555 return None;
556 };
557
558 match RawStateEventWithKeys::try_from_raw_state_event(decrypted_event.event.cast_unchecked()) {
561 Some(event) => {
562 trace!(?event_id, "Decrypted state event successfully.");
563 Some(event)
564 }
565 None => {
566 warn!(?event_id, "Failed to decrypt state event: decrypted state event is invalid");
567 None
568 }
569 }
570}
571
572#[cfg(test)]
573mod tests {
574 use assert_matches2::assert_matches;
575 use matrix_sdk_test::{
576 DEFAULT_TEST_ROOM_ID, JoinedRoomBuilder, SyncResponseBuilder, TestResult, async_test,
577 event_factory::EventFactory,
578 };
579 use ruma::{RoomVersionId, event_id, room_id, user_id};
580
581 use crate::test_utils::logged_in_base_client;
582
583 #[async_test]
584 async fn test_not_possible_to_overwrite_m_room_create() -> TestResult {
585 let sender = user_id!("@mnt_io:matrix.org");
586 let event_factory = EventFactory::new().sender(sender);
587 let mut response_builder = SyncResponseBuilder::new();
588 let room_id_0 = room_id!("!r0");
589 let room_id_1 = room_id!("!r1");
590 let room_id_2 = room_id!("!r2");
591
592 let client = logged_in_base_client(None).await;
593
594 {
598 let response = response_builder
599 .add_joined_room(
600 JoinedRoomBuilder::new(room_id_0)
601 .add_timeline_event(
602 event_factory.create(sender, RoomVersionId::try_from("42")?),
603 )
604 .add_timeline_event(
605 event_factory.create(sender, RoomVersionId::try_from("43")?),
606 ),
607 )
608 .add_joined_room(JoinedRoomBuilder::new(room_id_1).add_timeline_event(
609 event_factory.create(sender, RoomVersionId::try_from("44")?),
610 ))
611 .add_joined_room(JoinedRoomBuilder::new(room_id_2))
612 .build_sync_response();
613
614 assert!(client.receive_sync_response(response).await.is_ok());
615
616 assert_eq!(
619 client.get_room(room_id_0).unwrap().create_content().unwrap().room_version.as_str(),
620 "42"
621 );
622 assert_eq!(
624 client.get_room(room_id_1).unwrap().create_content().unwrap().room_version.as_str(),
625 "44"
626 );
627 assert!(client.get_room(room_id_2).unwrap().create_content().is_none());
629 }
630
631 {
635 let response = response_builder
636 .add_joined_room(JoinedRoomBuilder::new(room_id_0).add_timeline_event(
637 event_factory.create(sender, RoomVersionId::try_from("45")?),
638 ))
639 .add_joined_room(JoinedRoomBuilder::new(room_id_1).add_timeline_event(
640 event_factory.create(sender, RoomVersionId::try_from("46")?),
641 ))
642 .add_joined_room(JoinedRoomBuilder::new(room_id_2).add_timeline_event(
643 event_factory.create(sender, RoomVersionId::try_from("47")?),
644 ))
645 .build_sync_response();
646
647 assert!(client.receive_sync_response(response).await.is_ok());
648
649 assert_eq!(
652 client.get_room(room_id_0).unwrap().create_content().unwrap().room_version.as_str(),
653 "42"
654 );
655 assert_eq!(
658 client.get_room(room_id_1).unwrap().create_content().unwrap().room_version.as_str(),
659 "44"
660 );
661 assert_eq!(
663 client.get_room(room_id_2).unwrap().create_content().unwrap().room_version.as_str(),
664 "47"
665 );
666 }
667
668 Ok(())
669 }
670
671 #[async_test]
672 async fn test_check_room_upgrades_no_newly_tombstoned_rooms() {
673 let client = logged_in_base_client(None).await;
674
675 {
677 let response = SyncResponseBuilder::new()
678 .add_joined_room(JoinedRoomBuilder::new(room_id!("!r0")))
679 .build_sync_response();
680
681 assert!(client.receive_sync_response(response).await.is_ok());
682 }
683 }
684
685 #[async_test]
686 async fn test_check_room_upgrades_no_error() -> TestResult {
687 let sender = user_id!("@mnt_io:matrix.org");
688 let event_factory = EventFactory::new().sender(sender);
689 let mut response_builder = SyncResponseBuilder::new();
690 let room_id_0 = room_id!("!r0");
691 let room_id_1 = room_id!("!r1");
692 let room_id_2 = room_id!("!r2");
693
694 let client = logged_in_base_client(None).await;
695
696 {
698 let response = response_builder
699 .add_joined_room(JoinedRoomBuilder::new(room_id_0).add_timeline_event(
700 event_factory.create(sender, RoomVersionId::try_from("41")?),
702 ))
703 .build_sync_response();
704
705 assert!(client.receive_sync_response(response).await.is_ok());
706
707 let room_0 = client.get_room(room_id_0).unwrap();
708
709 assert!(room_0.predecessor_room().is_none());
710 assert!(room_0.successor_room().is_none());
711 }
712
713 {
715 let tombstone_event_id = event_id!("$ev0");
716 let response = response_builder
717 .add_joined_room(JoinedRoomBuilder::new(room_id_0).add_timeline_event(
718 event_factory.room_tombstone("hello", room_id_1).event_id(tombstone_event_id),
720 ))
721 .add_joined_room(
722 JoinedRoomBuilder::new(room_id_1).add_timeline_event(
723 event_factory
725 .create(sender, RoomVersionId::try_from("42")?)
726 .predecessor(room_id_0),
727 ),
728 )
729 .build_sync_response();
730
731 assert!(client.receive_sync_response(response).await.is_ok());
732
733 let room_0 = client.get_room(room_id_0).unwrap();
734
735 assert!(room_0.predecessor_room().is_none(), "room 0 must not have a predecessor");
736 assert_eq!(
737 room_0.successor_room().expect("room 0 must have a successor").room_id,
738 room_id_1,
739 "room 0 does not have the expected successor",
740 );
741
742 let room_1 = client.get_room(room_id_1).unwrap();
743
744 assert_eq!(
745 room_1.predecessor_room().expect("room 1 must have a predecessor").room_id,
746 room_id_0,
747 "room 1 does not have the expected predecessor",
748 );
749 assert!(room_1.successor_room().is_none(), "room 1 must not have a successor");
750 }
751
752 {
754 let tombstone_event_id = event_id!("$ev1");
755 let response = response_builder
756 .add_joined_room(JoinedRoomBuilder::new(room_id_1).add_timeline_event(
757 event_factory.room_tombstone("hello", room_id_2).event_id(tombstone_event_id),
759 ))
760 .add_joined_room(
761 JoinedRoomBuilder::new(room_id_2).add_timeline_event(
762 event_factory
764 .create(sender, RoomVersionId::try_from("43")?)
765 .predecessor(room_id_1),
766 ),
767 )
768 .build_sync_response();
769
770 assert!(client.receive_sync_response(response).await.is_ok());
771
772 let room_1 = client.get_room(room_id_1).unwrap();
773
774 assert_eq!(
775 room_1.predecessor_room().expect("room 1 must have a predecessor").room_id,
776 room_id_0,
777 "room 1 does not have the expected predecessor",
778 );
779 assert_eq!(
780 room_1.successor_room().expect("room 1 must have a successor").room_id,
781 room_id_2,
782 "room 1 does not have the expected successor",
783 );
784
785 let room_2 = client.get_room(room_id_2).unwrap();
786
787 assert_eq!(
788 room_2.predecessor_room().expect("room 2 must have a predecessor").room_id,
789 room_id_1,
790 "room 2 does not have the expected predecessor",
791 );
792 assert!(room_2.successor_room().is_none(), "room 2 must not have a successor");
793 }
794
795 Ok(())
796 }
797
798 #[async_test]
799 async fn test_check_room_upgrades_no_loop_within_misordered_rooms() -> TestResult {
800 let sender = user_id!("@mnt_io:matrix.org");
801 let event_factory = EventFactory::new().sender(sender);
802 let mut response_builder = SyncResponseBuilder::new();
803 let room_id_0 = room_id!("!r1");
806 let room_id_1 = room_id!("!r0");
807 let room_id_2 = room_id!("!r2");
808
809 let client = logged_in_base_client(None).await;
810
811 {
814 let response = response_builder
815 .add_joined_room(
817 JoinedRoomBuilder::new(room_id_0)
818 .add_timeline_event(
819 event_factory.create(sender, RoomVersionId::try_from("41")?),
821 )
822 .add_timeline_event(
823 event_factory
825 .room_tombstone("hello", room_id_1)
826 .event_id(event_id!("$ev0")),
827 ),
828 )
829 .add_joined_room(
831 JoinedRoomBuilder::new(room_id_1)
832 .add_timeline_event(
833 event_factory
835 .create(sender, RoomVersionId::try_from("42")?)
836 .predecessor(room_id_0),
837 )
838 .add_timeline_event(
839 event_factory
841 .room_tombstone("hello", room_id_2)
842 .event_id(event_id!("$ev1")),
843 ),
844 )
845 .add_joined_room(
847 JoinedRoomBuilder::new(room_id_2).add_timeline_event(
848 event_factory
850 .create(sender, RoomVersionId::try_from("43")?)
851 .predecessor(room_id_1),
852 ),
853 )
854 .build_sync_response();
855
856 {
858 let mut rooms = response.rooms.join.keys();
859
860 assert_eq!(rooms.next().unwrap(), room_id_1);
862 assert_eq!(rooms.next().unwrap(), room_id_0);
863 assert_eq!(rooms.next().unwrap(), room_id_2);
864 assert!(rooms.next().is_none());
865 }
866
867 assert!(client.receive_sync_response(response).await.is_ok());
869
870 let room_0 = client.get_room(room_id_0).unwrap();
871
872 assert!(room_0.predecessor_room().is_none(), "room 0 must not have a predecessor");
873 assert_eq!(
874 room_0.successor_room().expect("room 0 must have a successor").room_id,
875 room_id_1,
876 "room 0 does not have the expected successor",
877 );
878
879 let room_1 = client.get_room(room_id_1).unwrap();
880
881 assert_eq!(
882 room_1.predecessor_room().expect("room 1 must have a predecessor").room_id,
883 room_id_0,
884 "room 1 does not have the expected predecessor",
885 );
886 assert_eq!(
887 room_1.successor_room().expect("room 1 must have a successor").room_id,
888 room_id_2,
889 "room 1 does not have the expected successor",
890 );
891
892 let room_2 = client.get_room(room_id_2).unwrap();
893
894 assert_eq!(
895 room_2.predecessor_room().expect("room 2 must have a predecessor").room_id,
896 room_id_1,
897 "room 2 does not have the expected predecessor",
898 );
899 assert!(room_2.successor_room().is_none(), "room 2 must not have a successor");
900 }
901
902 Ok(())
903 }
904
905 #[async_test]
906 async fn test_check_room_upgrades_shortest_invalid_successor() -> TestResult {
907 let sender = user_id!("@mnt_io:matrix.org");
908 let event_factory = EventFactory::new().sender(sender);
909 let mut response_builder = SyncResponseBuilder::new();
910 let room_id_0 = room_id!("!r0");
911
912 let client = logged_in_base_client(None).await;
913
914 {
916 let tombstone_event_id = event_id!("$ev0");
917 let response = response_builder
918 .add_joined_room(
919 JoinedRoomBuilder::new(room_id_0).add_timeline_event(
922 event_factory
923 .room_tombstone("hello", room_id_0)
924 .event_id(tombstone_event_id),
925 ),
926 )
927 .build_sync_response();
928
929 assert!(client.receive_sync_response(response).await.is_ok());
931
932 let room_0 = client.get_room(room_id_0).unwrap();
934
935 assert!(room_0.predecessor_room().is_none(), "room 0 must not have a predecessor");
936 assert!(room_0.successor_room().is_none(), "room 0 must not have a successor");
937 }
938
939 Ok(())
940 }
941
942 #[async_test]
943 async fn test_check_room_upgrades_invalid_successor() -> TestResult {
944 let sender = user_id!("@mnt_io:matrix.org");
945 let event_factory = EventFactory::new().sender(sender);
946 let mut response_builder = SyncResponseBuilder::new();
947 let room_id_0 = room_id!("!r0");
948 let room_id_1 = room_id!("!r1");
949 let room_id_2 = room_id!("!r2");
950
951 let client = logged_in_base_client(None).await;
952
953 {
955 let tombstone_event_id = event_id!("$ev0");
956 let response = response_builder
957 .add_joined_room(JoinedRoomBuilder::new(room_id_0).add_timeline_event(
958 event_factory.room_tombstone("hello", room_id_1).event_id(tombstone_event_id),
960 ))
961 .add_joined_room(
962 JoinedRoomBuilder::new(room_id_1).add_timeline_event(
963 event_factory
965 .create(sender, RoomVersionId::try_from("42")?)
966 .predecessor(room_id_0),
967 ),
968 )
969 .build_sync_response();
970
971 assert!(client.receive_sync_response(response).await.is_ok());
972
973 let room_0 = client.get_room(room_id_0).unwrap();
974
975 assert!(room_0.predecessor_room().is_none(), "room 0 must not have a predecessor");
976 assert_eq!(
977 room_0.successor_room().expect("room 0 must have a successor").room_id,
978 room_id_1,
979 "room 0 does not have the expected successor",
980 );
981
982 let room_1 = client.get_room(room_id_1).unwrap();
983
984 assert_eq!(
985 room_1.predecessor_room().expect("room 1 must have a predecessor").room_id,
986 room_id_0,
987 "room 1 does not have the expected predecessor",
988 );
989 assert!(room_1.successor_room().is_none(), "room 1 must not have a successor");
990 }
991
992 {
994 let tombstone_event_id = event_id!("$ev1");
995 let response = response_builder
996 .add_joined_room(JoinedRoomBuilder::new(room_id_1).add_timeline_event(
997 event_factory.room_tombstone("hello", room_id_2).event_id(tombstone_event_id),
999 ))
1000 .add_joined_room(
1001 JoinedRoomBuilder::new(room_id_2)
1002 .add_timeline_event(
1003 event_factory
1005 .create(sender, RoomVersionId::try_from("43")?)
1006 .predecessor(room_id_1),
1007 )
1008 .add_timeline_event(
1009 event_factory
1011 .room_tombstone("hehe", room_id_0)
1012 .event_id(event_id!("$ev_foo")),
1013 ),
1014 )
1015 .build_sync_response();
1016
1017 assert!(client.receive_sync_response(response).await.is_ok());
1019
1020 let room_0 = client.get_room(room_id_0).unwrap();
1022
1023 assert!(room_0.predecessor_room().is_none(), "room 0 must not have a predecessor");
1024 assert_eq!(
1025 room_0.successor_room().expect("room 0 must have a successor").room_id,
1026 room_id_1,
1027 "room 0 does not have the expected successor",
1028 );
1029
1030 let room_1 = client.get_room(room_id_1).unwrap();
1031
1032 assert_eq!(
1033 room_1.predecessor_room().expect("room 1 must have a predecessor").room_id,
1034 room_id_0,
1035 "room 1 does not have the expected predecessor",
1036 );
1037 assert_eq!(
1038 room_1.successor_room().expect("room 1 must have a successor").room_id,
1039 room_id_2,
1040 "room 1 does not have the expected successor",
1041 );
1042
1043 let room_2 = client.get_room(room_id_2).unwrap();
1044
1045 assert_eq!(
1046 room_2.predecessor_room().expect("room 2 must have a predecessor").room_id,
1047 room_id_1,
1048 "room 2 does not have the expected predecessor",
1049 );
1050 assert!(room_2.successor_room().is_none(), "room 2 must not have a successor",);
1052 }
1053
1054 Ok(())
1055 }
1056
1057 #[async_test]
1058 async fn test_check_room_upgrades_shortest_invalid_predecessor() -> TestResult {
1059 let sender = user_id!("@mnt_io:matrix.org");
1060 let event_factory = EventFactory::new().sender(sender);
1061 let mut response_builder = SyncResponseBuilder::new();
1062 let room_id_0 = room_id!("!r0");
1063
1064 let client = logged_in_base_client(None).await;
1065
1066 {
1068 let tombstone_event_id = event_id!("$ev0");
1069 let response = response_builder
1070 .add_joined_room(
1071 JoinedRoomBuilder::new(room_id_0).add_timeline_event(
1074 event_factory
1075 .create(sender, RoomVersionId::try_from("42")?)
1076 .predecessor(room_id_0)
1077 .event_id(tombstone_event_id),
1078 ),
1079 )
1080 .build_sync_response();
1081
1082 assert!(client.receive_sync_response(response).await.is_ok());
1084
1085 let room_0 = client.get_room(room_id_0).unwrap();
1087
1088 assert!(room_0.predecessor_room().is_none(), "room 0 must not have a predecessor");
1089 assert!(room_0.successor_room().is_none(), "room 0 must not have a successor");
1090 assert_matches!(room_0.create_content(), Some(_), "room 0 must have a create content");
1091 }
1092
1093 Ok(())
1094 }
1095
1096 #[async_test]
1097 async fn test_check_room_upgrades_shortest_loop() -> TestResult {
1098 let sender = user_id!("@mnt_io:matrix.org");
1099 let event_factory = EventFactory::new().sender(sender);
1100 let mut response_builder = SyncResponseBuilder::new();
1101 let room_id_0 = room_id!("!r0");
1102
1103 let client = logged_in_base_client(None).await;
1104
1105 {
1107 let tombstone_event_id = event_id!("$ev0");
1108 let response = response_builder
1109 .add_joined_room(
1110 JoinedRoomBuilder::new(room_id_0)
1111 .add_timeline_event(
1112 event_factory
1114 .room_tombstone("hello", room_id_0)
1115 .event_id(tombstone_event_id),
1116 )
1117 .add_timeline_event(
1118 event_factory
1120 .create(sender, RoomVersionId::try_from("42")?)
1121 .predecessor(room_id_0),
1122 ),
1123 )
1124 .build_sync_response();
1125
1126 assert!(client.receive_sync_response(response).await.is_ok());
1128
1129 let room_0 = client.get_room(room_id_0).unwrap();
1131
1132 assert!(room_0.predecessor_room().is_none(), "room 0 must not have a predecessor");
1133 assert!(room_0.successor_room().is_none(), "room 0 must not have a successor");
1134 assert_matches!(room_0.create_content(), Some(_), "room 0 must have a create content");
1135 }
1136
1137 Ok(())
1138 }
1139
1140 #[async_test]
1141 async fn test_check_room_upgrades_loop() -> TestResult {
1142 let sender = user_id!("@mnt_io:matrix.org");
1143 let event_factory = EventFactory::new().sender(sender);
1144 let mut response_builder = SyncResponseBuilder::new();
1145 let room_id_0 = room_id!("!r0");
1146 let room_id_1 = room_id!("!r1");
1147 let room_id_2 = room_id!("!r2");
1148
1149 let client = logged_in_base_client(None).await;
1150
1151 {
1157 let response = response_builder
1158 .add_joined_room(
1159 JoinedRoomBuilder::new(room_id_0)
1160 .add_timeline_event(
1161 event_factory
1163 .create(sender, RoomVersionId::try_from("42")?)
1164 .predecessor(room_id_2),
1165 )
1166 .add_timeline_event(
1167 event_factory
1169 .room_tombstone("hello", room_id_1)
1170 .event_id(event_id!("$ev0")),
1171 ),
1172 )
1173 .add_joined_room(
1174 JoinedRoomBuilder::new(room_id_1)
1175 .add_timeline_event(
1176 event_factory
1178 .create(sender, RoomVersionId::try_from("43")?)
1179 .predecessor(room_id_0),
1180 )
1181 .add_timeline_event(
1182 event_factory
1184 .room_tombstone("hello", room_id_2)
1185 .event_id(event_id!("$ev1")),
1186 ),
1187 )
1188 .add_joined_room(
1189 JoinedRoomBuilder::new(room_id_2)
1190 .add_timeline_event(
1191 event_factory
1193 .create(sender, RoomVersionId::try_from("44")?)
1194 .predecessor(room_id_1),
1195 )
1196 .add_timeline_event(
1197 event_factory
1199 .room_tombstone("hello", room_id_0)
1200 .event_id(event_id!("$ev2")),
1201 ),
1202 )
1203 .build_sync_response();
1204
1205 assert!(client.receive_sync_response(response).await.is_ok());
1207
1208 let room_0 = client.get_room(room_id_0).unwrap();
1211
1212 assert_eq!(
1213 room_0.predecessor_room().expect("room 0 must have a predecessor").room_id,
1214 room_id_2,
1215 "room 0 does not have the expected predecessor"
1216 );
1217 assert_eq!(
1218 room_0.successor_room().expect("room 0 must have a successor").room_id,
1219 room_id_1,
1220 "room 0 does not have the expected successor",
1221 );
1222
1223 let room_1 = client.get_room(room_id_1).unwrap();
1224
1225 assert_eq!(
1226 room_1.predecessor_room().expect("room 1 must have a predecessor").room_id,
1227 room_id_0,
1228 "room 1 does not have the expected predecessor",
1229 );
1230 assert_eq!(
1231 room_1.successor_room().expect("room 1 must have a successor").room_id,
1232 room_id_2,
1233 "room 1 does not have the expected successor",
1234 );
1235
1236 let room_2 = client.get_room(room_id_2).unwrap();
1237
1238 assert!(room_2.predecessor_room().is_none(), "room 2 must not have a predecessor");
1240 assert!(room_2.successor_room().is_none(), "room 2 must not have a successor",);
1241 assert_matches!(room_2.create_content(), Some(_), "room 2 must have a create content");
1242 }
1243
1244 Ok(())
1245 }
1246
1247 #[async_test]
1248 async fn test_state_events_after_sync() -> TestResult {
1249 let user_id = user_id!("@u:u.to");
1251
1252 let client = logged_in_base_client(Some(user_id)).await;
1253 let mut sync_builder = SyncResponseBuilder::new();
1254
1255 let event_factory = EventFactory::new().sender(user_id);
1256
1257 let room_name = event_factory
1258 .room_topic("this is the test topic in the timeline")
1259 .event_id(event_id!("$2"))
1260 .into_raw_sync();
1261
1262 let response = sync_builder
1263 .add_joined_room(
1264 JoinedRoomBuilder::new(&DEFAULT_TEST_ROOM_ID)
1265 .add_timeline_event(room_name)
1266 .add_state_event(event_factory.create(user_id, RoomVersionId::V1))
1267 .add_state_event(event_factory.default_power_levels()),
1268 )
1269 .build_sync_response();
1270 client.receive_sync_response(response).await?;
1271
1272 let room = client.get_room(&DEFAULT_TEST_ROOM_ID).expect("Just-created room not found!");
1273
1274 assert!(room.power_levels().await.is_ok());
1276
1277 assert_eq!(room.topic().unwrap(), "this is the test topic in the timeline");
1279
1280 Ok(())
1281 }
1282}