1use std::collections::BTreeSet;
16
17use ruma::{
18 events::{
19 room::{create::RoomCreateEventContent, tombstone::RoomTombstoneEventContent},
20 AnySyncStateEvent, SyncStateEvent,
21 },
22 serde::Raw,
23 RoomId,
24};
25use serde::Deserialize;
26use tracing::warn;
27
28use super::Context;
29use crate::store::BaseStateStore;
30
31pub mod sync {
33 use std::{collections::BTreeSet, iter};
34
35 use ruma::{
36 events::{
37 room::member::{MembershipState, RoomMemberEventContent},
38 AnySyncTimelineEvent, SyncStateEvent,
39 },
40 OwnedUserId, RoomId, UserId,
41 };
42 use tracing::{error, instrument};
43
44 use super::{super::profiles, AnySyncStateEvent, Context, Raw};
45 use crate::{
46 store::{ambiguity_map::AmbiguityCache, BaseStateStore, Result as StoreResult},
47 RoomInfo,
48 };
49
50 pub fn collect(
52 raw_events: &[Raw<AnySyncStateEvent>],
53 ) -> (Vec<Raw<AnySyncStateEvent>>, Vec<AnySyncStateEvent>) {
54 super::collect(raw_events)
55 }
56
57 pub fn collect_from_timeline(
62 raw_events: &[Raw<AnySyncTimelineEvent>],
63 ) -> (Vec<Raw<AnySyncStateEvent>>, Vec<AnySyncStateEvent>) {
64 super::collect(raw_events.iter().filter_map(|raw_event| {
65 match raw_event.get_field::<&str>("state_key") {
67 Ok(Some(_)) => Some(raw_event.cast_ref()),
68 _ => None,
69 }
70 }))
71 }
72
73 #[instrument(skip_all, fields(room_id = ?room_info.room_id))]
83 pub async fn dispatch<U>(
84 context: &mut Context,
85 (raw_events, events): (&[Raw<AnySyncStateEvent>], &[AnySyncStateEvent]),
86 room_info: &mut RoomInfo,
87 ambiguity_cache: &mut AmbiguityCache,
88 new_users: &mut U,
89 state_store: &BaseStateStore,
90 ) -> StoreResult<()>
91 where
92 U: NewUsers,
93 {
94 for (raw_event, event) in iter::zip(raw_events, events) {
95 match event {
96 AnySyncStateEvent::RoomMember(member) => {
97 room_info.handle_state_event(event);
98
99 dispatch_room_member(
100 context,
101 &room_info.room_id,
102 member,
103 ambiguity_cache,
104 new_users,
105 )
106 .await?;
107 }
108
109 AnySyncStateEvent::RoomCreate(create) => {
110 if super::is_create_event_valid(
111 context,
112 room_info.room_id(),
113 create,
114 state_store,
115 ) {
116 room_info.handle_state_event(event);
117 } else {
118 error!(
119 room_id = ?room_info.room_id(),
120 ?create,
121 "`m.create.tombstone` event is invalid, it creates a loop"
122 );
123
124 continue;
127 }
128 }
129
130 AnySyncStateEvent::RoomTombstone(tombstone) => {
131 if super::is_tombstone_event_valid(
132 context,
133 room_info.room_id(),
134 tombstone,
135 state_store,
136 ) {
137 room_info.handle_state_event(event);
138 } else {
139 error!(
140 room_id = ?room_info.room_id(),
141 ?tombstone,
142 "`m.room.tombstone` event is invalid, it creates a loop"
143 );
144
145 continue;
148 }
149 }
150
151 _ => {
152 room_info.handle_state_event(event);
153 }
154 }
155
156 context
157 .state_changes
158 .state
159 .entry(room_info.room_id.to_owned())
160 .or_default()
161 .entry(event.event_type())
162 .or_default()
163 .insert(event.state_key().to_owned(), raw_event.clone());
164 }
165
166 Ok(())
167 }
168
169 async fn dispatch_room_member<U>(
171 context: &mut Context,
172 room_id: &RoomId,
173 event: &SyncStateEvent<RoomMemberEventContent>,
174 ambiguity_cache: &mut AmbiguityCache,
175 new_users: &mut U,
176 ) -> StoreResult<()>
177 where
178 U: NewUsers,
179 {
180 ambiguity_cache.handle_event(&context.state_changes, room_id, event).await?;
181
182 match event.membership() {
183 MembershipState::Join | MembershipState::Invite => {
184 new_users.insert(event.state_key());
185 }
186 _ => (),
187 }
188
189 profiles::upsert_or_delete(context, room_id, event);
190
191 Ok(())
192 }
193
194 trait NewUsers {
196 fn insert(&mut self, user_id: &UserId);
198 }
199
200 impl NewUsers for BTreeSet<OwnedUserId> {
201 fn insert(&mut self, user_id: &UserId) {
202 self.insert(user_id.to_owned());
203 }
204 }
205
206 impl NewUsers for () {
207 fn insert(&mut self, _user_id: &UserId) {}
208 }
209}
210
211pub mod stripped {
213 use std::{collections::BTreeMap, iter};
214
215 use ruma::{events::AnyStrippedStateEvent, push::Action};
216 use tracing::instrument;
217
218 use super::{
219 super::{notification, timeline},
220 Context, Raw,
221 };
222 use crate::{Result, Room, RoomInfo};
223
224 pub fn collect(
226 raw_events: &[Raw<AnyStrippedStateEvent>],
227 ) -> (Vec<Raw<AnyStrippedStateEvent>>, Vec<AnyStrippedStateEvent>) {
228 super::collect(raw_events)
229 }
230
231 #[instrument(skip_all, fields(room_id = ?room_info.room_id))]
248 pub(crate) async fn dispatch_invite_or_knock(
249 context: &mut Context,
250 (raw_events, events): (&[Raw<AnyStrippedStateEvent>], &[AnyStrippedStateEvent]),
251 room: &Room,
252 room_info: &mut RoomInfo,
253 mut notification: notification::Notification<'_>,
254 ) -> Result<()> {
255 let mut state_events = BTreeMap::new();
256
257 for (raw_event, event) in iter::zip(raw_events, events) {
258 room_info.handle_stripped_state_event(event);
259 state_events
260 .entry(event.event_type())
261 .or_insert_with(BTreeMap::new)
262 .insert(event.state_key().to_owned(), raw_event.clone());
263 }
264
265 context
266 .state_changes
267 .stripped_state
268 .insert(room_info.room_id().to_owned(), state_events.clone());
269
270 if let Some(push_condition_room_ctx) =
273 timeline::get_push_room_context(context, room, room_info, notification.state_store)
274 .await?
275 {
276 let room_id = room.room_id();
277
278 for event in state_events.values().flat_map(|map| map.values()) {
280 notification.push_notification_from_event_if(
281 room_id,
282 &push_condition_room_ctx,
283 event,
284 Action::should_notify,
285 );
286 }
287 }
288
289 Ok(())
290 }
291}
292
293fn collect<'a, I, T>(raw_events: I) -> (Vec<Raw<T>>, Vec<T>)
294where
295 I: IntoIterator<Item = &'a Raw<T>>,
296 T: Deserialize<'a> + 'a,
297{
298 raw_events
299 .into_iter()
300 .filter_map(|raw_event| match raw_event.deserialize() {
301 Ok(event) => Some((raw_event.clone(), event)),
302 Err(e) => {
303 warn!("Couldn't deserialize stripped state event: {e}");
304 None
305 }
306 })
307 .unzip()
308}
309
310pub fn is_create_event_valid(
312 context: &mut Context,
313 room_id: &RoomId,
314 event: &SyncStateEvent<RoomCreateEventContent>,
315 state_store: &BaseStateStore,
316) -> bool {
317 let mut already_seen = BTreeSet::new();
318 already_seen.insert(room_id.to_owned());
319
320 let Some(mut predecessor_room_id) = event
321 .as_original()
322 .and_then(|event| Some(event.content.predecessor.as_ref()?.room_id.clone()))
323 else {
324 return true;
326 };
327
328 loop {
329 if already_seen.contains(AsRef::<RoomId>::as_ref(&predecessor_room_id)) {
333 return false;
335 }
336
337 already_seen.insert(predecessor_room_id.clone());
338
339 let Some(next_predecessor_room_id) = context
342 .state_changes
343 .room_infos
344 .get(&predecessor_room_id)
345 .and_then(|room_info| Some(room_info.create()?.predecessor.as_ref()?.room_id.clone()))
346 .or_else(|| {
347 state_store
348 .room(&predecessor_room_id)
349 .and_then(|room| Some(room.predecessor_room()?.room_id))
350 })
351 else {
352 break;
354 };
355
356 predecessor_room_id = next_predecessor_room_id;
357 }
358
359 true
360}
361
362pub fn is_tombstone_event_valid(
364 context: &mut Context,
365 room_id: &RoomId,
366 event: &SyncStateEvent<RoomTombstoneEventContent>,
367 state_store: &BaseStateStore,
368) -> bool {
369 let mut already_seen = BTreeSet::new();
370 already_seen.insert(room_id.to_owned());
371
372 let Some(mut successor_room_id) =
373 event.as_original().map(|event| event.content.replacement_room.clone())
374 else {
375 return true;
377 };
378
379 loop {
380 if already_seen.contains(AsRef::<RoomId>::as_ref(&successor_room_id)) {
383 return false;
385 }
386
387 already_seen.insert(successor_room_id.clone());
388
389 let Some(next_successor_room_id) = context
391 .state_changes
392 .room_infos
393 .get(&successor_room_id)
394 .and_then(|room_info| Some(room_info.tombstone()?.replacement_room.clone()))
395 .or_else(|| {
396 state_store
397 .room(&successor_room_id)
398 .and_then(|room| Some(room.successor_room()?.room_id))
399 })
400 else {
401 break;
403 };
404
405 successor_room_id = next_successor_room_id;
406 }
407
408 true
409}
410
411#[cfg(test)]
412mod tests {
413 use matrix_sdk_test::{
414 async_test, event_factory::EventFactory, JoinedRoomBuilder, StateTestEvent,
415 SyncResponseBuilder, DEFAULT_TEST_ROOM_ID,
416 };
417 use ruma::{event_id, room_id, user_id, RoomVersionId};
418
419 use crate::test_utils::logged_in_base_client;
420
421 #[async_test]
422 async fn test_not_possible_to_overwrite_m_room_create() {
423 let sender = user_id!("@mnt_io:matrix.org");
424 let event_factory = EventFactory::new().sender(sender);
425 let mut response_builder = SyncResponseBuilder::new();
426 let room_id_0 = room_id!("!r0");
427 let room_id_1 = room_id!("!r1");
428 let room_id_2 = room_id!("!r2");
429
430 let client = logged_in_base_client(None).await;
431
432 {
436 let response = response_builder
437 .add_joined_room(
438 JoinedRoomBuilder::new(room_id_0)
439 .add_timeline_event(
440 event_factory.create(sender, RoomVersionId::try_from("42").unwrap()),
441 )
442 .add_timeline_event(
443 event_factory.create(sender, RoomVersionId::try_from("43").unwrap()),
444 ),
445 )
446 .add_joined_room(JoinedRoomBuilder::new(room_id_1).add_timeline_event(
447 event_factory.create(sender, RoomVersionId::try_from("44").unwrap()),
448 ))
449 .add_joined_room(JoinedRoomBuilder::new(room_id_2))
450 .build_sync_response();
451
452 assert!(client.receive_sync_response(response).await.is_ok());
453
454 assert_eq!(
457 client.get_room(room_id_0).unwrap().create_content().unwrap().room_version.as_str(),
458 "42"
459 );
460 assert_eq!(
462 client.get_room(room_id_1).unwrap().create_content().unwrap().room_version.as_str(),
463 "44"
464 );
465 assert!(client.get_room(room_id_2).unwrap().create_content().is_none());
467 }
468
469 {
473 let response = response_builder
474 .add_joined_room(JoinedRoomBuilder::new(room_id_0).add_timeline_event(
475 event_factory.create(sender, RoomVersionId::try_from("45").unwrap()),
476 ))
477 .add_joined_room(JoinedRoomBuilder::new(room_id_1).add_timeline_event(
478 event_factory.create(sender, RoomVersionId::try_from("46").unwrap()),
479 ))
480 .add_joined_room(JoinedRoomBuilder::new(room_id_2).add_timeline_event(
481 event_factory.create(sender, RoomVersionId::try_from("47").unwrap()),
482 ))
483 .build_sync_response();
484
485 assert!(client.receive_sync_response(response).await.is_ok());
486
487 assert_eq!(
490 client.get_room(room_id_0).unwrap().create_content().unwrap().room_version.as_str(),
491 "42"
492 );
493 assert_eq!(
496 client.get_room(room_id_1).unwrap().create_content().unwrap().room_version.as_str(),
497 "44"
498 );
499 assert_eq!(
501 client.get_room(room_id_2).unwrap().create_content().unwrap().room_version.as_str(),
502 "47"
503 );
504 }
505 }
506
507 #[async_test]
508 async fn test_check_room_upgrades_no_newly_tombstoned_rooms() {
509 let client = logged_in_base_client(None).await;
510
511 {
513 let response = SyncResponseBuilder::new()
514 .add_joined_room(JoinedRoomBuilder::new(room_id!("!r0")))
515 .build_sync_response();
516
517 assert!(client.receive_sync_response(response).await.is_ok());
518 }
519 }
520
521 #[async_test]
522 async fn test_check_room_upgrades_no_error() {
523 let sender = user_id!("@mnt_io:matrix.org");
524 let event_factory = EventFactory::new().sender(sender);
525 let mut response_builder = SyncResponseBuilder::new();
526 let room_id_0 = room_id!("!r0");
527 let room_id_1 = room_id!("!r1");
528 let room_id_2 = room_id!("!r2");
529
530 let client = logged_in_base_client(None).await;
531
532 {
534 let response = response_builder
535 .add_joined_room(JoinedRoomBuilder::new(room_id_0).add_timeline_event(
536 event_factory.create(sender, RoomVersionId::try_from("41").unwrap()),
538 ))
539 .build_sync_response();
540
541 assert!(client.receive_sync_response(response).await.is_ok());
542
543 let room_0 = client.get_room(room_id_0).unwrap();
544
545 assert!(room_0.predecessor_room().is_none());
546 assert!(room_0.successor_room().is_none());
547 }
548
549 {
551 let tombstone_event_id = event_id!("$ev0");
552 let response = response_builder
553 .add_joined_room(JoinedRoomBuilder::new(room_id_0).add_timeline_event(
554 event_factory.room_tombstone("hello", room_id_1).event_id(tombstone_event_id),
556 ))
557 .add_joined_room(
558 JoinedRoomBuilder::new(room_id_1).add_timeline_event(
559 event_factory
561 .create(sender, RoomVersionId::try_from("42").unwrap())
562 .predecessor(room_id_0, tombstone_event_id),
563 ),
564 )
565 .build_sync_response();
566
567 assert!(client.receive_sync_response(response).await.is_ok());
568
569 let room_0 = client.get_room(room_id_0).unwrap();
570
571 assert!(room_0.predecessor_room().is_none(), "room 0 must not have a predecessor");
572 assert_eq!(
573 room_0.successor_room().expect("room 0 must have a successor").room_id,
574 room_id_1,
575 "room 0 does not have the expected successor",
576 );
577
578 let room_1 = client.get_room(room_id_1).unwrap();
579
580 assert_eq!(
581 room_1.predecessor_room().expect("room 1 must have a predecessor").room_id,
582 room_id_0,
583 "room 1 does not have the expected predecessor",
584 );
585 assert!(room_1.successor_room().is_none(), "room 1 must not have a successor");
586 }
587
588 {
590 let tombstone_event_id = event_id!("$ev1");
591 let response = response_builder
592 .add_joined_room(JoinedRoomBuilder::new(room_id_1).add_timeline_event(
593 event_factory.room_tombstone("hello", room_id_2).event_id(tombstone_event_id),
595 ))
596 .add_joined_room(
597 JoinedRoomBuilder::new(room_id_2).add_timeline_event(
598 event_factory
600 .create(sender, RoomVersionId::try_from("43").unwrap())
601 .predecessor(room_id_1, tombstone_event_id),
602 ),
603 )
604 .build_sync_response();
605
606 assert!(client.receive_sync_response(response).await.is_ok());
607
608 let room_1 = client.get_room(room_id_1).unwrap();
609
610 assert_eq!(
611 room_1.predecessor_room().expect("room 1 must have a predecessor").room_id,
612 room_id_0,
613 "room 1 does not have the expected predecessor",
614 );
615 assert_eq!(
616 room_1.successor_room().expect("room 1 must have a successor").room_id,
617 room_id_2,
618 "room 1 does not have the expected successor",
619 );
620
621 let room_2 = client.get_room(room_id_2).unwrap();
622
623 assert_eq!(
624 room_2.predecessor_room().expect("room 2 must have a predecessor").room_id,
625 room_id_1,
626 "room 2 does not have the expected predecessor",
627 );
628 assert!(room_2.successor_room().is_none(), "room 2 must not have a successor");
629 }
630 }
631
632 #[async_test]
633 async fn test_check_room_upgrades_no_loop_within_misordered_rooms() {
634 let sender = user_id!("@mnt_io:matrix.org");
635 let event_factory = EventFactory::new().sender(sender);
636 let mut response_builder = SyncResponseBuilder::new();
637 let room_id_0 = room_id!("!r1");
640 let room_id_1 = room_id!("!r0");
641 let room_id_2 = room_id!("!r2");
642
643 let client = logged_in_base_client(None).await;
644
645 {
648 let response = response_builder
649 .add_joined_room(
651 JoinedRoomBuilder::new(room_id_0)
652 .add_timeline_event(
653 event_factory.create(sender, RoomVersionId::try_from("41").unwrap()),
655 )
656 .add_timeline_event(
657 event_factory
659 .room_tombstone("hello", room_id_1)
660 .event_id(event_id!("$ev0")),
661 ),
662 )
663 .add_joined_room(
665 JoinedRoomBuilder::new(room_id_1)
666 .add_timeline_event(
667 event_factory
669 .create(sender, RoomVersionId::try_from("42").unwrap())
670 .predecessor(room_id_0, event_id!("$ev0")),
671 )
672 .add_timeline_event(
673 event_factory
675 .room_tombstone("hello", room_id_2)
676 .event_id(event_id!("$ev1")),
677 ),
678 )
679 .add_joined_room(
681 JoinedRoomBuilder::new(room_id_2).add_timeline_event(
682 event_factory
684 .create(sender, RoomVersionId::try_from("43").unwrap())
685 .predecessor(room_id_1, event_id!("$ev1")),
686 ),
687 )
688 .build_sync_response();
689
690 {
692 let mut rooms = response.rooms.join.keys();
693
694 assert_eq!(rooms.next().unwrap(), room_id_1);
696 assert_eq!(rooms.next().unwrap(), room_id_0);
697 assert_eq!(rooms.next().unwrap(), room_id_2);
698 assert!(rooms.next().is_none());
699 }
700
701 assert!(client.receive_sync_response(response).await.is_ok());
703
704 let room_0 = client.get_room(room_id_0).unwrap();
705
706 assert!(room_0.predecessor_room().is_none(), "room 0 must not have a predecessor");
707 assert_eq!(
708 room_0.successor_room().expect("room 0 must have a successor").room_id,
709 room_id_1,
710 "room 0 does not have the expected successor",
711 );
712
713 let room_1 = client.get_room(room_id_1).unwrap();
714
715 assert_eq!(
716 room_1.predecessor_room().expect("room 1 must have a predecessor").room_id,
717 room_id_0,
718 "room 1 does not have the expected predecessor",
719 );
720 assert_eq!(
721 room_1.successor_room().expect("room 1 must have a successor").room_id,
722 room_id_2,
723 "room 1 does not have the expected successor",
724 );
725
726 let room_2 = client.get_room(room_id_2).unwrap();
727
728 assert_eq!(
729 room_2.predecessor_room().expect("room 2 must have a predecessor").room_id,
730 room_id_1,
731 "room 2 does not have the expected predecessor",
732 );
733 assert!(room_2.successor_room().is_none(), "room 2 must not have a successor");
734 }
735 }
736
737 #[async_test]
738 async fn test_check_room_upgrades_shortest_invalid_successor() {
739 let sender = user_id!("@mnt_io:matrix.org");
740 let event_factory = EventFactory::new().sender(sender);
741 let mut response_builder = SyncResponseBuilder::new();
742 let room_id_0 = room_id!("!r0");
743
744 let client = logged_in_base_client(None).await;
745
746 {
748 let tombstone_event_id = event_id!("$ev0");
749 let response = response_builder
750 .add_joined_room(
751 JoinedRoomBuilder::new(room_id_0).add_timeline_event(
754 event_factory
755 .room_tombstone("hello", room_id_0)
756 .event_id(tombstone_event_id),
757 ),
758 )
759 .build_sync_response();
760
761 assert!(client.receive_sync_response(response).await.is_ok());
763
764 let room_0 = client.get_room(room_id_0).unwrap();
766
767 assert!(room_0.predecessor_room().is_none(), "room 0 must not have a predecessor");
768 assert!(room_0.successor_room().is_none(), "room 0 must not have a successor");
769 }
770 }
771
772 #[async_test]
773 async fn test_check_room_upgrades_invalid_successor() {
774 let sender = user_id!("@mnt_io:matrix.org");
775 let event_factory = EventFactory::new().sender(sender);
776 let mut response_builder = SyncResponseBuilder::new();
777 let room_id_0 = room_id!("!r0");
778 let room_id_1 = room_id!("!r1");
779 let room_id_2 = room_id!("!r2");
780
781 let client = logged_in_base_client(None).await;
782
783 {
785 let tombstone_event_id = event_id!("$ev0");
786 let response = response_builder
787 .add_joined_room(JoinedRoomBuilder::new(room_id_0).add_timeline_event(
788 event_factory.room_tombstone("hello", room_id_1).event_id(tombstone_event_id),
790 ))
791 .add_joined_room(
792 JoinedRoomBuilder::new(room_id_1).add_timeline_event(
793 event_factory
795 .create(sender, RoomVersionId::try_from("42").unwrap())
796 .predecessor(room_id_0, tombstone_event_id),
797 ),
798 )
799 .build_sync_response();
800
801 assert!(client.receive_sync_response(response).await.is_ok());
802
803 let room_0 = client.get_room(room_id_0).unwrap();
804
805 assert!(room_0.predecessor_room().is_none(), "room 0 must not have a predecessor");
806 assert_eq!(
807 room_0.successor_room().expect("room 0 must have a successor").room_id,
808 room_id_1,
809 "room 0 does not have the expected successor",
810 );
811
812 let room_1 = client.get_room(room_id_1).unwrap();
813
814 assert_eq!(
815 room_1.predecessor_room().expect("room 1 must have a predecessor").room_id,
816 room_id_0,
817 "room 1 does not have the expected predecessor",
818 );
819 assert!(room_1.successor_room().is_none(), "room 1 must not have a successor");
820 }
821
822 {
824 let tombstone_event_id = event_id!("$ev1");
825 let response = response_builder
826 .add_joined_room(JoinedRoomBuilder::new(room_id_1).add_timeline_event(
827 event_factory.room_tombstone("hello", room_id_2).event_id(tombstone_event_id),
829 ))
830 .add_joined_room(
831 JoinedRoomBuilder::new(room_id_2)
832 .add_timeline_event(
833 event_factory
835 .create(sender, RoomVersionId::try_from("43").unwrap())
836 .predecessor(room_id_1, tombstone_event_id),
837 )
838 .add_timeline_event(
839 event_factory
841 .room_tombstone("hehe", room_id_0)
842 .event_id(event_id!("$ev_foo")),
843 ),
844 )
845 .build_sync_response();
846
847 assert!(client.receive_sync_response(response).await.is_ok());
849
850 let room_0 = client.get_room(room_id_0).unwrap();
852
853 assert!(room_0.predecessor_room().is_none(), "room 0 must not have a predecessor");
854 assert_eq!(
855 room_0.successor_room().expect("room 0 must have a successor").room_id,
856 room_id_1,
857 "room 0 does not have the expected successor",
858 );
859
860 let room_1 = client.get_room(room_id_1).unwrap();
861
862 assert_eq!(
863 room_1.predecessor_room().expect("room 1 must have a predecessor").room_id,
864 room_id_0,
865 "room 1 does not have the expected predecessor",
866 );
867 assert_eq!(
868 room_1.successor_room().expect("room 1 must have a successor").room_id,
869 room_id_2,
870 "room 1 does not have the expected successor",
871 );
872
873 let room_2 = client.get_room(room_id_2).unwrap();
874
875 assert_eq!(
876 room_2.predecessor_room().expect("room 2 must have a predecessor").room_id,
877 room_id_1,
878 "room 2 does not have the expected predecessor",
879 );
880 assert!(room_2.successor_room().is_none(), "room 2 must not have a successor",);
882 }
883 }
884
885 #[async_test]
886 async fn test_check_room_upgrades_shortest_invalid_predecessor() {
887 let sender = user_id!("@mnt_io:matrix.org");
888 let event_factory = EventFactory::new().sender(sender);
889 let mut response_builder = SyncResponseBuilder::new();
890 let room_id_0 = room_id!("!r0");
891
892 let client = logged_in_base_client(None).await;
893
894 {
896 let tombstone_event_id = event_id!("$ev0");
897 let response = response_builder
898 .add_joined_room(
899 JoinedRoomBuilder::new(room_id_0).add_timeline_event(
902 event_factory
903 .create(sender, RoomVersionId::try_from("42").unwrap())
904 .predecessor(room_id_0, tombstone_event_id)
905 .event_id(tombstone_event_id),
906 ),
907 )
908 .build_sync_response();
909
910 assert!(client.receive_sync_response(response).await.is_ok());
912
913 let room_0 = client.get_room(room_id_0).unwrap();
915
916 assert!(room_0.predecessor_room().is_none(), "room 0 must not have a predecessor");
917 assert!(room_0.successor_room().is_none(), "room 0 must not have a successor");
918 }
919 }
920
921 #[async_test]
922 async fn test_check_room_upgrades_shortest_loop() {
923 let sender = user_id!("@mnt_io:matrix.org");
924 let event_factory = EventFactory::new().sender(sender);
925 let mut response_builder = SyncResponseBuilder::new();
926 let room_id_0 = room_id!("!r0");
927
928 let client = logged_in_base_client(None).await;
929
930 {
932 let tombstone_event_id = event_id!("$ev0");
933 let response = response_builder
934 .add_joined_room(
935 JoinedRoomBuilder::new(room_id_0)
936 .add_timeline_event(
937 event_factory
939 .room_tombstone("hello", room_id_0)
940 .event_id(tombstone_event_id),
941 )
942 .add_timeline_event(
943 event_factory
945 .create(sender, RoomVersionId::try_from("42").unwrap())
946 .predecessor(room_id_0, tombstone_event_id),
947 ),
948 )
949 .build_sync_response();
950
951 assert!(client.receive_sync_response(response).await.is_ok());
953
954 let room_0 = client.get_room(room_id_0).unwrap();
956
957 assert!(room_0.predecessor_room().is_none(), "room 0 must not have a predecessor");
958 assert!(room_0.successor_room().is_none(), "room 0 must not have a successor");
959 }
960 }
961
962 #[async_test]
963 async fn test_check_room_upgrades_loop() {
964 let sender = user_id!("@mnt_io:matrix.org");
965 let event_factory = EventFactory::new().sender(sender);
966 let mut response_builder = SyncResponseBuilder::new();
967 let room_id_0 = room_id!("!r0");
968 let room_id_1 = room_id!("!r1");
969 let room_id_2 = room_id!("!r2");
970
971 let client = logged_in_base_client(None).await;
972
973 {
979 let response = response_builder
980 .add_joined_room(
981 JoinedRoomBuilder::new(room_id_0)
982 .add_timeline_event(
983 event_factory
985 .create(sender, RoomVersionId::try_from("42").unwrap())
986 .predecessor(room_id_2, event_id!("$ev2")),
987 )
988 .add_timeline_event(
989 event_factory
991 .room_tombstone("hello", room_id_1)
992 .event_id(event_id!("$ev0")),
993 ),
994 )
995 .add_joined_room(
996 JoinedRoomBuilder::new(room_id_1)
997 .add_timeline_event(
998 event_factory
1000 .create(sender, RoomVersionId::try_from("43").unwrap())
1001 .predecessor(room_id_0, event_id!("$ev0")),
1002 )
1003 .add_timeline_event(
1004 event_factory
1006 .room_tombstone("hello", room_id_2)
1007 .event_id(event_id!("$ev1")),
1008 ),
1009 )
1010 .add_joined_room(
1011 JoinedRoomBuilder::new(room_id_2)
1012 .add_timeline_event(
1013 event_factory
1015 .create(sender, RoomVersionId::try_from("44").unwrap())
1016 .predecessor(room_id_1, event_id!("$ev1")),
1017 )
1018 .add_timeline_event(
1019 event_factory
1021 .room_tombstone("hello", room_id_0)
1022 .event_id(event_id!("$ev2")),
1023 ),
1024 )
1025 .build_sync_response();
1026
1027 assert!(client.receive_sync_response(response).await.is_ok());
1029
1030 let room_0 = client.get_room(room_id_0).unwrap();
1033
1034 assert_eq!(
1035 room_0.predecessor_room().expect("room 0 must have a predecessor").room_id,
1036 room_id_2,
1037 "room 0 does not have the expected predecessor"
1038 );
1039 assert_eq!(
1040 room_0.successor_room().expect("room 0 must have a successor").room_id,
1041 room_id_1,
1042 "room 0 does not have the expected successor",
1043 );
1044
1045 let room_1 = client.get_room(room_id_1).unwrap();
1046
1047 assert_eq!(
1048 room_1.predecessor_room().expect("room 1 must have a predecessor").room_id,
1049 room_id_0,
1050 "room 1 does not have the expected predecessor",
1051 );
1052 assert_eq!(
1053 room_1.successor_room().expect("room 1 must have a successor").room_id,
1054 room_id_2,
1055 "room 1 does not have the expected successor",
1056 );
1057
1058 let room_2 = client.get_room(room_id_2).unwrap();
1059
1060 assert!(room_2.predecessor_room().is_none(), "room 2 must not have a predecessor");
1062 assert!(room_2.successor_room().is_none(), "room 2 must not have a successor",);
1063 }
1064 }
1065
1066 #[async_test]
1067 async fn test_state_events_after_sync() {
1068 let user_id = user_id!("@u:u.to");
1070
1071 let client = logged_in_base_client(Some(user_id)).await;
1072 let mut sync_builder = SyncResponseBuilder::new();
1073
1074 let room_name = EventFactory::new()
1075 .sender(user_id)
1076 .room_topic("this is the test topic in the timeline")
1077 .event_id(event_id!("$2"))
1078 .into_raw_sync();
1079
1080 let response = sync_builder
1081 .add_joined_room(
1082 JoinedRoomBuilder::new(&DEFAULT_TEST_ROOM_ID)
1083 .add_timeline_event(room_name)
1084 .add_state_event(StateTestEvent::PowerLevels),
1085 )
1086 .build_sync_response();
1087 client.receive_sync_response(response).await.unwrap();
1088
1089 let room = client.get_room(&DEFAULT_TEST_ROOM_ID).expect("Just-created room not found!");
1090
1091 assert!(room.power_levels().await.is_ok());
1093
1094 assert_eq!(room.topic().unwrap(), "this is the test topic in the timeline");
1096 }
1097}