1#[cfg(feature = "e2e-encryption")]
18use matrix_sdk_common::deserialized_responses::ProcessedToDeviceEvent;
19use matrix_sdk_common::timer;
20use ruma::{
21 OwnedRoomId, api::client::sync::sync_events::v5 as http, events::receipt::SyncReceiptEvent,
22 serde::Raw,
23};
24use tokio::sync::MutexGuard;
25use tracing::{instrument, trace};
26
27use super::BaseClient;
28use crate::{
29 RequestedRequiredStates,
30 error::Result,
31 response_processors as processors,
32 store::ambiguity_map::AmbiguityCache,
33 sync::{RoomUpdates, SyncResponse},
34};
35
36impl BaseClient {
37 #[cfg(feature = "e2e-encryption")]
45 pub async fn process_sliding_sync_e2ee(
46 &self,
47 to_device: Option<&http::response::ToDevice>,
48 e2ee: &http::response::E2EE,
49 state_store_guard: &MutexGuard<'_, ()>,
50 ) -> Result<Option<Vec<ProcessedToDeviceEvent>>> {
51 if to_device.is_none() && e2ee.is_empty() {
52 return Ok(None);
53 }
54
55 trace!(
56 to_device_events =
57 to_device.map(|to_device| to_device.events.len()).unwrap_or_default(),
58 device_one_time_keys_count = e2ee.device_one_time_keys_count.len(),
59 device_unused_fallback_key_types =
60 e2ee.device_unused_fallback_key_types.as_ref().map(|v| v.len()),
61 "Processing sliding sync e2ee events",
62 );
63
64 let olm_machine = self.olm_machine().await;
65
66 let context = processors::Context::default();
67
68 let processors::e2ee::to_device::Output { processed_to_device_events } =
69 processors::e2ee::to_device::from_msc4186(
70 to_device,
71 e2ee,
72 olm_machine.as_ref(),
73 &self.decryption_settings,
74 )
75 .await?;
76
77 processors::changes::save_and_apply(
78 context,
79 &self.state_store,
80 state_store_guard,
81 &self.ignore_user_list_changes,
82 None,
83 )
84 .await?;
85
86 Ok(Some(processed_to_device_events))
87 }
88
89 #[instrument(skip_all, level = "trace")]
96 pub async fn process_sliding_sync(
97 &self,
98 response: &http::Response,
99 requested_required_states: &RequestedRequiredStates,
100 state_store_guard: &MutexGuard<'_, ()>,
101 ) -> Result<SyncResponse> {
102 let http::Response { rooms, lists, extensions, .. } = response;
103
104 trace!(
105 rooms = rooms.len(),
106 lists = lists.len(),
107 has_extensions = !extensions.is_empty(),
108 "Processing sliding sync room events"
109 );
110
111 if rooms.is_empty() && extensions.is_empty() {
112 return Ok(SyncResponse::default());
115 }
116
117 let _timer = timer!(tracing::Level::TRACE, "_method");
118
119 let mut context = processors::Context::default();
120
121 let state_store = self.state_store.clone();
122 let mut ambiguity_cache = AmbiguityCache::new(state_store.inner.clone());
123
124 let global_account_data_processor =
125 processors::account_data::global(&extensions.account_data.global);
126 let push_rules = self.get_push_rules(&global_account_data_processor).await?;
127
128 let mut room_updates = RoomUpdates::default();
129 let mut notifications = Default::default();
130
131 let user_id = self
132 .session_meta()
133 .expect("Sliding sync shouldn't run without an authenticated user")
134 .user_id
135 .to_owned();
136
137 for (room_id, room_response) in rooms {
138 let Some((room_info, room_update)) = processors::room::msc4186::update_any_room(
139 &mut context,
140 &user_id,
141 processors::room::RoomCreationData::new(
142 room_id,
143 requested_required_states,
144 &mut ambiguity_cache,
145 ),
146 room_response,
147 &extensions.account_data.rooms,
148 #[cfg(feature = "e2e-encryption")]
149 processors::e2ee::E2EE::new(
150 self.olm_machine().await.as_ref(),
151 &self.decryption_settings,
152 self.handle_verification_events,
153 ),
154 processors::notification::Notification::new(
155 &push_rules,
156 &mut notifications,
157 &self.state_store,
158 ),
159 )
160 .await?
161 else {
162 continue;
163 };
164
165 context.state_changes.add_room(room_info);
166
167 let room_id = room_id.to_owned();
168
169 use processors::room::msc4186::RoomUpdateKind;
170
171 match room_update {
172 RoomUpdateKind::Joined(joined_room_update) => {
173 room_updates.joined.insert(room_id, joined_room_update);
174 }
175 RoomUpdateKind::Left(left_room_update) => {
176 room_updates.left.insert(room_id, left_room_update);
177 }
178 RoomUpdateKind::Invited(invited_room_update) => {
179 room_updates.invited.insert(room_id, invited_room_update);
180 }
181 RoomUpdateKind::Knocked(knocked_room_update) => {
182 room_updates.knocked.insert(room_id, knocked_room_update);
183 }
184 }
185 }
186
187 processors::room::msc4186::extensions::dispatch_typing_ephemeral_events(
191 &extensions.typing,
192 &mut room_updates.joined,
193 );
194
195 processors::room::msc4186::extensions::room_account_data(
197 &mut context,
198 &extensions.account_data,
199 &mut room_updates,
200 &self.state_store,
201 );
202
203 global_account_data_processor.apply(&mut context, &state_store).await;
204
205 context.state_changes.ambiguity_maps = ambiguity_cache.cache;
206
207 processors::changes::save_and_apply(
209 context,
210 &self.state_store,
211 state_store_guard,
212 &self.ignore_user_list_changes,
213 None,
214 )
215 .await?;
216
217 let mut context = processors::Context::default();
218
219 processors::room::display_name::update_for_rooms(
222 &mut context,
223 &room_updates,
224 &self.state_store,
225 )
226 .await;
227
228 processors::changes::save_only(context, &self.state_store, state_store_guard).await?;
230
231 Ok(SyncResponse {
232 rooms: room_updates,
233 notifications,
234 presence: Default::default(),
235 account_data: extensions.account_data.global.clone(),
236 to_device: Default::default(),
237 })
238 }
239
240 #[doc(hidden)]
243 pub async fn process_sliding_sync_receipts_extension_for_room(
244 &self,
245 room_id: &OwnedRoomId,
246 response: &http::Response,
247 state_store_guard: &MutexGuard<'_, ()>,
248 ) -> Result<Option<Raw<SyncReceiptEvent>>> {
249 let mut context = processors::Context::default();
250
251 let mut save_context = false;
252
253 let receipt_ephemeral_event = if let Some(receipt_ephemeral_event) =
255 response.extensions.receipts.rooms.get(room_id)
256 {
257 processors::room::msc4186::extensions::dispatch_receipt_ephemeral_event_for_room(
258 &mut context,
259 room_id,
260 receipt_ephemeral_event,
261 );
262 save_context = true;
263 Some(receipt_ephemeral_event.clone())
264 } else {
265 None
266 };
267
268 if save_context {
270 processors::changes::save_only(context, &self.state_store, state_store_guard).await?;
271 }
272
273 Ok(receipt_ephemeral_event)
274 }
275}
276
277#[cfg(all(test, not(target_family = "wasm")))]
278mod tests {
279 use std::collections::{BTreeMap, HashSet};
280
281 use assert_matches::assert_matches;
282 use matrix_sdk_test::async_test;
283 use ruma::{
284 JsOption, MxcUri, OwnedRoomId, OwnedUserId, RoomAliasId, RoomId, UserId,
285 api::client::sync::sync_events::UnreadNotificationsCount,
286 assign, event_id,
287 events::{
288 GlobalAccountDataEventContent, StateEventContent, StateEventType,
289 direct::{DirectEventContent, DirectUserIdentifier, OwnedDirectUserIdentifier},
290 room::{
291 avatar::RoomAvatarEventContent,
292 canonical_alias::RoomCanonicalAliasEventContent,
293 encryption::RoomEncryptionEventContent,
294 member::{MembershipState, RoomMemberEventContent},
295 name::RoomNameEventContent,
296 pinned_events::RoomPinnedEventsEventContent,
297 },
298 },
299 mxc_uri, owned_event_id, owned_mxc_uri, owned_user_id, room_alias_id, room_id,
300 serde::Raw,
301 uint, user_id,
302 };
303 use serde_json::json;
304
305 use super::http;
306 use crate::{
307 BaseClient, DmRoomDefinition, EncryptionState, RequestedRequiredStates,
308 RoomInfoNotableUpdate, RoomState, SessionMeta,
309 client::ThreadingSupport,
310 room::{RoomHero, RoomInfoNotableUpdateReasons},
311 store::{RoomLoadSettings, StoreConfig},
312 test_utils::logged_in_base_client,
313 };
314
315 #[async_test]
316 async fn test_invited_state_without_update_emits_invited_room() {
317 let client = logged_in_base_client(None).await;
318 let room_id = room_id!("!invite:e.uk");
319 let user_id = client.session_meta().unwrap().user_id.to_owned();
320
321 let mut room = http::response::Room::new();
322 room.invite_state = Some(invite_state_for(&user_id, MembershipState::Invite));
323
324 let response = response_with_room(room_id, room);
325
326 let sync_resp = client
327 .process_sliding_sync(
328 &response,
329 &RequestedRequiredStates::default(),
330 &client.state_store_lock().lock().await,
331 )
332 .await
333 .unwrap();
334
335 assert!(sync_resp.rooms.invited.contains_key(room_id));
336 }
337
338 use matrix_sdk_common::cross_process_lock::CrossProcessLockConfig;
339 use ruma::events::AnyStrippedStateEvent;
340
341 fn invite_state_for(
342 user_id: &UserId,
343 membership: MembershipState,
344 ) -> Vec<Raw<AnyStrippedStateEvent>> {
345 let content = RoomMemberEventContent::new(membership);
346
347 let raw: Raw<AnyStrippedStateEvent> = Raw::from_json_string(
348 serde_json::json!({
349 "type": "m.room.member",
350 "state_key": user_id,
351 "content": content,
352 })
353 .to_string(),
354 )
355 .unwrap();
356
357 vec![raw]
358 }
359
360 #[async_test]
361 async fn test_knocked_state_emits_invited_room() {
362 let client = logged_in_base_client(None).await;
363 let room_id = room_id!("!knock:e.uk");
364 let user_id = client.session_meta().unwrap().user_id.to_owned();
365
366 let mut room = http::response::Room::new();
367 room.invite_state = Some(invite_state_for(&user_id, MembershipState::Knock));
368
369 let response = response_with_room(room_id, room);
370
371 let sync_resp = client
372 .process_sliding_sync(
373 &response,
374 &RequestedRequiredStates::default(),
375 &client.state_store_lock().lock().await,
376 )
377 .await
378 .unwrap();
379
380 assert!(sync_resp.rooms.invited.contains_key(room_id));
382 }
383
384 #[async_test]
385 async fn test_notification_count_set() {
386 let client = logged_in_base_client(None).await;
387
388 let mut response = http::Response::new("42".to_owned());
389 let room_id = room_id!("!room:example.org");
390 let count = assign!(UnreadNotificationsCount::default(), {
391 highlight_count: Some(uint!(13)),
392 notification_count: Some(uint!(37)),
393 });
394
395 response.rooms.insert(
396 room_id.to_owned(),
397 assign!(http::response::Room::new(), {
398 unread_notifications: count.clone()
399 }),
400 );
401
402 let sync_response = client
403 .process_sliding_sync(
404 &response,
405 &RequestedRequiredStates::default(),
406 &client.state_store_lock().lock().await,
407 )
408 .await
409 .expect("Failed to process sync");
410
411 let room = sync_response.rooms.joined.get(room_id).unwrap();
413 assert_eq!(room.unread_notifications, count.clone().into());
414
415 let room = client.get_room(room_id).expect("found room");
417 assert_eq!(room.unread_notification_counts(), count.into());
418 }
419
420 #[async_test]
421 async fn test_can_process_empty_sliding_sync_response() {
422 let client = logged_in_base_client(None).await;
423 let empty_response = http::Response::new("5".to_owned());
424 client
425 .process_sliding_sync(
426 &empty_response,
427 &RequestedRequiredStates::default(),
428 &client.state_store_lock().lock().await,
429 )
430 .await
431 .expect("Failed to process sync");
432 }
433
434 #[async_test]
435 async fn test_room_with_unspecified_state_is_added_to_client_and_joined_list() {
436 let client = logged_in_base_client(None).await;
438 let room_id = room_id!("!r:e.uk");
439
440 let mut room = http::response::Room::new();
443 room.joined_count = Some(uint!(41));
444 let response = response_with_room(room_id, room);
445 let sync_resp = client
446 .process_sliding_sync(
447 &response,
448 &RequestedRequiredStates::default(),
449 &client.state_store_lock().lock().await,
450 )
451 .await
452 .expect("Failed to process sync");
453
454 let client_room = client.get_room(room_id).expect("No room found");
456 assert_eq!(client_room.room_id(), room_id);
457 assert_eq!(client_room.joined_members_count(), 41);
458 assert_eq!(client_room.state(), RoomState::Joined);
459
460 assert!(sync_resp.rooms.joined.contains_key(room_id));
462 assert!(!sync_resp.rooms.left.contains_key(room_id));
463 assert!(!sync_resp.rooms.invited.contains_key(room_id));
464 }
465
466 #[async_test]
467 async fn test_missing_room_name_event() {
468 let client = logged_in_base_client(None).await;
470 let room_id = room_id!("!r:e.uk");
471
472 let mut room = http::response::Room::new();
475 room.name = Some("little room".to_owned());
476 let response = response_with_room(room_id, room);
477 let sync_resp = client
478 .process_sliding_sync(
479 &response,
480 &RequestedRequiredStates::default(),
481 &client.state_store_lock().lock().await,
482 )
483 .await
484 .expect("Failed to process sync");
485
486 let client_room = client.get_room(room_id).expect("No room found");
488 assert!(client_room.name().is_none());
489 assert_eq!(
490 client_room.compute_display_name().await.unwrap().into_inner().to_string(),
491 "Empty Room"
492 );
493 assert_eq!(client_room.state(), RoomState::Joined);
494
495 assert!(sync_resp.rooms.joined.contains_key(room_id));
497 assert!(!sync_resp.rooms.left.contains_key(room_id));
498 assert!(!sync_resp.rooms.invited.contains_key(room_id));
499 assert!(!sync_resp.rooms.knocked.contains_key(room_id));
500 }
501
502 #[async_test]
503 async fn test_room_name_event() {
504 let client = logged_in_base_client(None).await;
506 let room_id = room_id!("!r:e.uk");
507
508 let mut room = http::response::Room::new();
511
512 room.name = Some("little room".to_owned());
513 set_room_name(&mut room, user_id!("@a:b.c"), "The Name".to_owned());
514
515 let response = response_with_room(room_id, room);
516 client
517 .process_sliding_sync(
518 &response,
519 &RequestedRequiredStates::default(),
520 &client.state_store_lock().lock().await,
521 )
522 .await
523 .expect("Failed to process sync");
524
525 let client_room = client.get_room(room_id).expect("No room found");
527 assert_eq!(client_room.name().as_deref(), Some("The Name"));
528 assert_eq!(
529 client_room.compute_display_name().await.unwrap().into_inner().to_string(),
530 "The Name"
531 );
532 }
533
534 #[async_test]
535 async fn test_missing_invited_room_name_event() {
536 let client = logged_in_base_client(None).await;
538 let room_id = room_id!("!r:e.uk");
539 let user_id = user_id!("@w:e.uk");
540 let inviter = user_id!("@john:mastodon.org");
541
542 let mut room = http::response::Room::new();
545 set_room_invited(&mut room, inviter, user_id);
546 room.name = Some("name from sliding sync response".to_owned());
547 let response = response_with_room(room_id, room);
548 let sync_resp = client
549 .process_sliding_sync(
550 &response,
551 &RequestedRequiredStates::default(),
552 &client.state_store_lock().lock().await,
553 )
554 .await
555 .expect("Failed to process sync");
556
557 let client_room = client.get_room(room_id).expect("No room found");
559 assert!(client_room.name().is_none());
560
561 assert_eq!(client_room.compute_display_name().await.unwrap().into_inner().to_string(), "w");
563
564 assert_eq!(client_room.state(), RoomState::Invited);
565
566 assert!(!sync_resp.rooms.joined.contains_key(room_id));
568 assert!(!sync_resp.rooms.left.contains_key(room_id));
569 assert!(sync_resp.rooms.invited.contains_key(room_id));
570 assert!(!sync_resp.rooms.knocked.contains_key(room_id));
571 }
572
573 #[async_test]
574 async fn test_invited_room_name_event() {
575 let client = logged_in_base_client(None).await;
577 let room_id = room_id!("!r:e.uk");
578 let user_id = user_id!("@w:e.uk");
579 let inviter = user_id!("@john:mastodon.org");
580
581 let mut room = http::response::Room::new();
584
585 set_room_invited(&mut room, inviter, user_id);
586
587 room.name = Some("name from sliding sync response".to_owned());
588 set_room_name(&mut room, user_id!("@a:b.c"), "The Name".to_owned());
589
590 let response = response_with_room(room_id, room);
591 client
592 .process_sliding_sync(
593 &response,
594 &RequestedRequiredStates::default(),
595 &client.state_store_lock().lock().await,
596 )
597 .await
598 .expect("Failed to process sync");
599
600 let client_room = client.get_room(room_id).expect("No room found");
602 assert_eq!(client_room.name().as_deref(), Some("The Name"));
603 assert_eq!(
604 client_room.compute_display_name().await.unwrap().into_inner().to_string(),
605 "The Name"
606 );
607 }
608
609 #[async_test]
610 async fn test_receiving_a_knocked_room_membership_event_creates_a_knocked_room() {
611 let client = logged_in_base_client(None).await;
613 let room_id = room_id!("!r:e.uk");
614 let user_id = client.session_meta().unwrap().user_id.to_owned();
615
616 let mut room = http::response::Room::new();
619 set_room_knocked(&mut room, &user_id);
620
621 let response = response_with_room(room_id, room);
622 client
623 .process_sliding_sync(
624 &response,
625 &RequestedRequiredStates::default(),
626 &client.state_store_lock().lock().await,
627 )
628 .await
629 .expect("Failed to process sync");
630
631 let client_room = client.get_room(room_id).expect("No room found");
633 assert_eq!(client_room.state(), RoomState::Knocked);
634 }
635
636 #[async_test]
637 async fn test_receiving_a_knocked_room_membership_event_with_wrong_state_key_creates_an_invited_room()
638 {
639 let client = logged_in_base_client(None).await;
641 let room_id = room_id!("!r:e.uk");
642 let user_id = user_id!("@w:e.uk");
643
644 let mut room = http::response::Room::new();
646 set_room_knocked(&mut room, user_id);
647
648 let response = response_with_room(room_id, room);
649 client
650 .process_sliding_sync(
651 &response,
652 &RequestedRequiredStates::default(),
653 &client.state_store_lock().lock().await,
654 )
655 .await
656 .expect("Failed to process sync");
657
658 let client_room = client.get_room(room_id).expect("No room found");
661 assert_eq!(client_room.state(), RoomState::Invited);
662 }
663
664 #[async_test]
665 async fn test_receiving_an_unknown_room_membership_event_in_invite_state_creates_an_invited_room()
666 {
667 let client = logged_in_base_client(None).await;
669 let room_id = room_id!("!r:e.uk");
670 let user_id = client.session_meta().unwrap().user_id.to_owned();
671
672 let mut room = http::response::Room::new();
674 let event = Raw::new(&json!({
675 "type": "m.room.member",
676 "sender": user_id,
677 "content": {
678 "is_direct": true,
679 "membership": "join",
680 },
681 "state_key": user_id,
682 }))
683 .expect("Failed to make raw event")
684 .cast_unchecked();
685 room.invite_state = Some(vec![event]);
686
687 let response = response_with_room(room_id, room);
688 client
689 .process_sliding_sync(
690 &response,
691 &RequestedRequiredStates::default(),
692 &client.state_store_lock().lock().await,
693 )
694 .await
695 .expect("Failed to process sync");
696
697 let client_room = client.get_room(room_id).expect("No room found");
699 assert_eq!(client_room.state(), RoomState::Invited);
700 }
701
702 #[async_test]
703 async fn test_left_a_room_from_required_state_event() {
704 let client = logged_in_base_client(None).await;
706 let room_id = room_id!("!r:e.uk");
707 let user_id = user_id!("@u:e.uk");
708
709 let mut room = http::response::Room::new();
711 set_room_joined(&mut room, user_id);
712 let response = response_with_room(room_id, room);
713 client
714 .process_sliding_sync(
715 &response,
716 &RequestedRequiredStates::default(),
717 &client.state_store_lock().lock().await,
718 )
719 .await
720 .expect("Failed to process sync");
721 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Joined);
722
723 let mut room = http::response::Room::new();
725 set_room_left(&mut room, user_id);
726 let response = response_with_room(room_id, room);
727 let sync_resp = client
728 .process_sliding_sync(
729 &response,
730 &RequestedRequiredStates::default(),
731 &client.state_store_lock().lock().await,
732 )
733 .await
734 .expect("Failed to process sync");
735
736 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Left);
738
739 assert!(!sync_resp.rooms.joined.contains_key(room_id));
741 assert!(sync_resp.rooms.left.contains_key(room_id));
742 assert!(!sync_resp.rooms.invited.contains_key(room_id));
743 assert!(!sync_resp.rooms.knocked.contains_key(room_id));
744 }
745
746 #[async_test]
747 async fn test_kick_or_ban_updates_room_to_left() {
748 for membership in [MembershipState::Leave, MembershipState::Ban] {
749 let room_id = room_id!("!r:e.uk");
750 let user_a_id = user_id!("@a:e.uk");
751 let user_b_id = user_id!("@b:e.uk");
752 let client = logged_in_base_client(Some(user_a_id)).await;
753
754 let mut room = http::response::Room::new();
756 set_room_joined(&mut room, user_a_id);
757 let response = response_with_room(room_id, room);
758 client
759 .process_sliding_sync(
760 &response,
761 &RequestedRequiredStates::default(),
762 &client.state_store_lock().lock().await,
763 )
764 .await
765 .expect("Failed to process sync");
766 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Joined);
767
768 let mut room = http::response::Room::new();
770 room.required_state.push(make_state_event(
771 user_b_id,
772 user_a_id.as_str(),
773 RoomMemberEventContent::new(membership.clone()),
774 None,
775 ));
776 let response = response_with_room(room_id, room);
777 let sync_resp = client
778 .process_sliding_sync(
779 &response,
780 &RequestedRequiredStates::default(),
781 &client.state_store_lock().lock().await,
782 )
783 .await
784 .expect("Failed to process sync");
785
786 match membership {
787 MembershipState::Leave => {
788 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Left);
790 }
791 MembershipState::Ban => {
792 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Banned);
794 }
795 _ => panic!("Unexpected membership state found: {membership}"),
796 }
797
798 assert!(!sync_resp.rooms.joined.contains_key(room_id));
800 assert!(sync_resp.rooms.left.contains_key(room_id));
801 assert!(!sync_resp.rooms.invited.contains_key(room_id));
802 assert!(!sync_resp.rooms.knocked.contains_key(room_id));
803 }
804 }
805
806 #[async_test]
807 async fn test_left_a_room_from_timeline_state_event() {
808 let client = logged_in_base_client(None).await;
810 let room_id = room_id!("!r:e.uk");
811 let user_id = user_id!("@u:e.uk");
812
813 let mut room = http::response::Room::new();
815 set_room_joined(&mut room, user_id);
816 let response = response_with_room(room_id, room);
817 client
818 .process_sliding_sync(
819 &response,
820 &RequestedRequiredStates::default(),
821 &client.state_store_lock().lock().await,
822 )
823 .await
824 .expect("Failed to process sync");
825 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Joined);
826
827 let mut room = http::response::Room::new();
829 set_room_left_as_timeline_event(&mut room, user_id);
830 let response = response_with_room(room_id, room);
831 client
832 .process_sliding_sync(
833 &response,
834 &RequestedRequiredStates::default(),
835 &client.state_store_lock().lock().await,
836 )
837 .await
838 .expect("Failed to process sync");
839
840 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Joined);
842 }
843
844 #[async_test]
845 async fn test_can_be_reinvited_to_a_left_room() {
846 let client = logged_in_base_client(None).await;
850 let room_id = room_id!("!r:e.uk");
851 let user_id = user_id!("@u:e.uk");
852
853 let mut room = http::response::Room::new();
855 set_room_joined(&mut room, user_id);
856 let response = response_with_room(room_id, room);
857 client
858 .process_sliding_sync(
859 &response,
860 &RequestedRequiredStates::default(),
861 &client.state_store_lock().lock().await,
862 )
863 .await
864 .expect("Failed to process sync");
865 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Joined);
867
868 let mut room = http::response::Room::new();
870 set_room_left(&mut room, user_id);
871 let response = response_with_room(room_id, room);
872 client
873 .process_sliding_sync(
874 &response,
875 &RequestedRequiredStates::default(),
876 &client.state_store_lock().lock().await,
877 )
878 .await
879 .expect("Failed to process sync");
880 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Left);
882
883 let mut room = http::response::Room::new();
885 set_room_invited(&mut room, user_id, user_id);
886 let response = response_with_room(room_id, room);
887 client
888 .process_sliding_sync(
889 &response,
890 &RequestedRequiredStates::default(),
891 &client.state_store_lock().lock().await,
892 )
893 .await
894 .expect("Failed to process sync");
895
896 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Invited);
898 }
899
900 #[async_test]
901 async fn test_other_person_leaving_a_dm_is_reflected_in_their_membership_and_direct_targets() {
902 let room_id = room_id!("!r:e.uk");
903 let user_a_id = user_id!("@a:e.uk");
904 let user_b_id = user_id!("@b:e.uk");
905
906 let client = logged_in_base_client(None).await;
908 create_dm(&client, room_id, user_a_id, user_b_id, MembershipState::Join).await;
909
910 assert!(
912 direct_targets(&client, room_id).contains(<&DirectUserIdentifier>::from(user_b_id))
913 );
914 assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Join);
915
916 update_room_membership(&client, room_id, user_b_id, MembershipState::Leave).await;
918
919 assert!(
923 direct_targets(&client, room_id).contains(<&DirectUserIdentifier>::from(user_b_id))
924 );
925 assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Leave);
926 }
927
928 #[async_test]
929 async fn test_other_person_refusing_invite_to_a_dm_is_reflected_in_their_membership_and_direct_targets()
930 {
931 let room_id = room_id!("!r:e.uk");
932 let user_a_id = user_id!("@a:e.uk");
933 let user_b_id = user_id!("@b:e.uk");
934
935 let client = logged_in_base_client(None).await;
937 create_dm(&client, room_id, user_a_id, user_b_id, MembershipState::Invite).await;
938
939 assert!(
941 direct_targets(&client, room_id).contains(<&DirectUserIdentifier>::from(user_b_id))
942 );
943 assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Invite);
944
945 update_room_membership(&client, room_id, user_b_id, MembershipState::Leave).await;
947
948 assert!(
952 direct_targets(&client, room_id).contains(<&DirectUserIdentifier>::from(user_b_id))
953 );
954 assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Leave);
955 }
956
957 #[async_test]
958 async fn test_members_count_in_a_dm_where_other_person_has_joined() {
959 let room_id = room_id!("!r:bar.org");
960 let user_a_id = user_id!("@a:bar.org");
961 let user_b_id = user_id!("@b:bar.org");
962
963 let client = logged_in_base_client(None).await;
965 create_dm(&client, room_id, user_a_id, user_b_id, MembershipState::Join).await;
966
967 assert_eq!(membership(&client, room_id, user_a_id).await, MembershipState::Join);
969
970 assert!(
972 direct_targets(&client, room_id).contains(<&DirectUserIdentifier>::from(user_b_id))
973 );
974 assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Join);
975
976 let room = client.get_room(room_id).unwrap();
977
978 assert_eq!(room.active_members_count(), 2);
979 assert_eq!(room.joined_members_count(), 2);
980 assert_eq!(room.invited_members_count(), 0);
981 }
982
983 #[async_test]
984 async fn test_members_count_in_a_dm_where_other_person_is_invited() {
985 let room_id = room_id!("!r:bar.org");
986 let user_a_id = user_id!("@a:bar.org");
987 let user_b_id = user_id!("@b:bar.org");
988
989 let client = logged_in_base_client(None).await;
991 create_dm(&client, room_id, user_a_id, user_b_id, MembershipState::Invite).await;
992
993 assert_eq!(membership(&client, room_id, user_a_id).await, MembershipState::Join);
995
996 assert!(
998 direct_targets(&client, room_id).contains(<&DirectUserIdentifier>::from(user_b_id))
999 );
1000 assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Invite);
1001
1002 let room = client.get_room(room_id).unwrap();
1003
1004 assert_eq!(room.active_members_count(), 2);
1005 assert_eq!(room.joined_members_count(), 1);
1006 assert_eq!(room.invited_members_count(), 1);
1007 }
1008
1009 #[async_test]
1010 async fn test_avatar_is_found_when_processing_sliding_sync_response() {
1011 let client = logged_in_base_client(None).await;
1013 let room_id = room_id!("!r:e.uk");
1014
1015 let room = {
1017 let mut room = http::response::Room::new();
1018 room.avatar = JsOption::from_option(Some(owned_mxc_uri!("mxc://e.uk/med1")));
1019
1020 room
1021 };
1022 let response = response_with_room(room_id, room);
1023 client
1024 .process_sliding_sync(
1025 &response,
1026 &RequestedRequiredStates::default(),
1027 &client.state_store_lock().lock().await,
1028 )
1029 .await
1030 .expect("Failed to process sync");
1031
1032 let client_room = client.get_room(room_id).expect("No room found");
1034 assert_eq!(
1035 client_room.avatar_url().expect("No avatar URL").media_id().expect("No media ID"),
1036 "med1"
1037 );
1038 }
1039
1040 #[async_test]
1041 async fn test_avatar_can_be_unset_when_processing_sliding_sync_response() {
1042 let client = logged_in_base_client(None).await;
1044 let room_id = room_id!("!r:e.uk");
1045
1046 let room = {
1050 let mut room = http::response::Room::new();
1051 room.avatar = JsOption::from_option(Some(owned_mxc_uri!("mxc://e.uk/med1")));
1052
1053 room
1054 };
1055 let response = response_with_room(room_id, room);
1056 client
1057 .process_sliding_sync(
1058 &response,
1059 &RequestedRequiredStates::default(),
1060 &client.state_store_lock().lock().await,
1061 )
1062 .await
1063 .expect("Failed to process sync");
1064
1065 let client_room = client.get_room(room_id).expect("No room found");
1067 assert_eq!(
1068 client_room.avatar_url().expect("No avatar URL").media_id().expect("No media ID"),
1069 "med1"
1070 );
1071
1072 let room = http::response::Room::new();
1076 let response = response_with_room(room_id, room);
1077 client
1078 .process_sliding_sync(
1079 &response,
1080 &RequestedRequiredStates::default(),
1081 &client.state_store_lock().lock().await,
1082 )
1083 .await
1084 .expect("Failed to process sync");
1085
1086 let client_room = client.get_room(room_id).expect("No room found");
1088 assert_eq!(
1089 client_room.avatar_url().expect("No avatar URL").media_id().expect("No media ID"),
1090 "med1"
1091 );
1092
1093 let room = {
1097 let mut room = http::response::Room::new();
1098 room.avatar = JsOption::Null;
1099
1100 room
1101 };
1102 let response = response_with_room(room_id, room);
1103 client
1104 .process_sliding_sync(
1105 &response,
1106 &RequestedRequiredStates::default(),
1107 &client.state_store_lock().lock().await,
1108 )
1109 .await
1110 .expect("Failed to process sync");
1111
1112 let client_room = client.get_room(room_id).expect("No room found");
1114 assert!(client_room.avatar_url().is_none());
1115 }
1116
1117 #[async_test]
1118 async fn test_avatar_is_found_from_required_state_when_processing_sliding_sync_response() {
1119 let client = logged_in_base_client(None).await;
1121 let room_id = room_id!("!r:e.uk");
1122 let user_id = user_id!("@u:e.uk");
1123
1124 let room = room_with_avatar(mxc_uri!("mxc://e.uk/med1"), user_id);
1126 let response = response_with_room(room_id, room);
1127 client
1128 .process_sliding_sync(
1129 &response,
1130 &RequestedRequiredStates::default(),
1131 &client.state_store_lock().lock().await,
1132 )
1133 .await
1134 .expect("Failed to process sync");
1135
1136 let client_room = client.get_room(room_id).expect("No room found");
1138 assert_eq!(
1139 client_room.avatar_url().expect("No avatar URL").media_id().expect("No media ID"),
1140 "med1"
1141 );
1142 }
1143
1144 #[async_test]
1145 async fn test_invitation_room_is_added_to_client_and_invite_list() {
1146 let client = logged_in_base_client(None).await;
1148 let room_id = room_id!("!r:e.uk");
1149 let user_id = user_id!("@u:e.uk");
1150
1151 let mut room_info_notable_update = client.room_info_notable_update_receiver();
1152
1153 let mut room = http::response::Room::new();
1155 set_room_invited(&mut room, user_id, user_id);
1156 let response = response_with_room(room_id, room);
1157 let sync_resp = client
1158 .process_sliding_sync(
1159 &response,
1160 &RequestedRequiredStates::default(),
1161 &client.state_store_lock().lock().await,
1162 )
1163 .await
1164 .expect("Failed to process sync");
1165
1166 let client_room = client.get_room(room_id).expect("No room found");
1168 assert_eq!(client_room.room_id(), room_id);
1169 assert_eq!(client_room.state(), RoomState::Invited);
1170
1171 assert!(!sync_resp.rooms.invited[room_id].invite_state.is_empty());
1173 assert!(!sync_resp.rooms.joined.contains_key(room_id));
1174
1175 assert_matches!(
1176 room_info_notable_update.recv().await,
1177 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons }) => {
1178 assert_eq!(received_room_id, room_id);
1179 assert!(reasons.contains(RoomInfoNotableUpdateReasons::MEMBERSHIP));
1181 }
1182 );
1183 assert_matches!(
1184 room_info_notable_update.recv().await,
1185 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons }) => {
1186 assert_eq!(received_room_id, room_id);
1187 assert!(reasons.contains(RoomInfoNotableUpdateReasons::DISPLAY_NAME));
1189 }
1190 );
1191 assert!(room_info_notable_update.is_empty());
1192 }
1193
1194 #[async_test]
1195 async fn test_knock_room_is_added_to_client_and_knock_list() {
1196 let client = logged_in_base_client(None).await;
1198 let room_id = room_id!("!r:e.uk");
1199 let user_id = user_id!("@u:e.uk");
1200
1201 let mut room_info_notable_update = client.room_info_notable_update_receiver();
1202
1203 let mut room = http::response::Room::new();
1205 set_room_knocked(&mut room, user_id);
1206 let response = response_with_room(room_id, room);
1207 let sync_resp = client
1208 .process_sliding_sync(
1209 &response,
1210 &RequestedRequiredStates::default(),
1211 &client.state_store_lock().lock().await,
1212 )
1213 .await
1214 .expect("Failed to process sync");
1215
1216 let client_room = client.get_room(room_id).expect("No room found");
1218 assert_eq!(client_room.room_id(), room_id);
1219 assert_eq!(client_room.state(), RoomState::Knocked);
1220
1221 assert!(!sync_resp.rooms.knocked[room_id].knock_state.is_empty());
1223 assert!(!sync_resp.rooms.joined.contains_key(room_id));
1224
1225 assert_matches!(
1226 room_info_notable_update.recv().await,
1227 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons }) => {
1228 assert_eq!(received_room_id, room_id);
1229 assert!(reasons.contains(RoomInfoNotableUpdateReasons::MEMBERSHIP));
1231 }
1232 );
1233 assert_matches!(
1234 room_info_notable_update.recv().await,
1235 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons }) => {
1236 assert_eq!(received_room_id, room_id);
1237 assert!(reasons.contains(RoomInfoNotableUpdateReasons::DISPLAY_NAME));
1239 }
1240 );
1241 assert!(room_info_notable_update.is_empty());
1242 }
1243
1244 #[async_test]
1245 async fn test_avatar_is_found_in_invitation_room_when_processing_sliding_sync_response() {
1246 let client = logged_in_base_client(None).await;
1248 let room_id = room_id!("!r:e.uk");
1249 let user_id = user_id!("@u:e.uk");
1250
1251 let mut room = room_with_avatar(mxc_uri!("mxc://e.uk/med1"), user_id);
1253 set_room_invited(&mut room, user_id, user_id);
1254 let response = response_with_room(room_id, room);
1255 client
1256 .process_sliding_sync(
1257 &response,
1258 &RequestedRequiredStates::default(),
1259 &client.state_store_lock().lock().await,
1260 )
1261 .await
1262 .expect("Failed to process sync");
1263
1264 let client_room = client.get_room(room_id).expect("No room found");
1266 assert_eq!(
1267 client_room.avatar_url().expect("No avatar URL").media_id().expect("No media ID"),
1268 "med1"
1269 );
1270 }
1271
1272 #[async_test]
1273 async fn test_canonical_alias_is_found_in_invitation_room_when_processing_sliding_sync_response()
1274 {
1275 let client = logged_in_base_client(None).await;
1277 let room_id = room_id!("!r:e.uk");
1278 let user_id = user_id!("@u:e.uk");
1279 let room_alias_id = room_alias_id!("#myroom:e.uk");
1280
1281 let mut room = room_with_canonical_alias(room_alias_id, user_id);
1283 set_room_invited(&mut room, user_id, user_id);
1284 let response = response_with_room(room_id, room);
1285 client
1286 .process_sliding_sync(
1287 &response,
1288 &RequestedRequiredStates::default(),
1289 &client.state_store_lock().lock().await,
1290 )
1291 .await
1292 .expect("Failed to process sync");
1293
1294 let client_room = client.get_room(room_id).expect("No room found");
1296 assert_eq!(client_room.canonical_alias(), Some(room_alias_id.to_owned()));
1297 }
1298
1299 #[async_test]
1300 async fn test_display_name_from_sliding_sync_doesnt_override_alias() {
1301 let client = logged_in_base_client(None).await;
1303 let room_id = room_id!("!r:e.uk");
1304 let user_id = user_id!("@u:e.uk");
1305 let room_alias_id = room_alias_id!("#myroom:e.uk");
1306
1307 let mut room = room_with_canonical_alias(room_alias_id, user_id);
1310 room.name = Some("This came from the server".to_owned());
1311 let response = response_with_room(room_id, room);
1312 client
1313 .process_sliding_sync(
1314 &response,
1315 &RequestedRequiredStates::default(),
1316 &client.state_store_lock().lock().await,
1317 )
1318 .await
1319 .expect("Failed to process sync");
1320
1321 let client_room = client.get_room(room_id).expect("No room found");
1323 assert_eq!(
1324 client_room.compute_display_name().await.unwrap().into_inner().to_string(),
1325 "myroom"
1326 );
1327 assert!(client_room.name().is_none());
1328 }
1329
1330 #[async_test]
1331 async fn test_display_name_is_cached_and_emits_a_notable_update_reason() {
1332 let client = logged_in_base_client(None).await;
1333 let user_id = user_id!("@u:e.uk");
1334 let room_id = room_id!("!r:e.uk");
1335
1336 let mut room_info_notable_update = client.room_info_notable_update_receiver();
1337
1338 let room = room_with_name("Hello World", user_id);
1339 let response = response_with_room(room_id, room);
1340 client
1341 .process_sliding_sync(
1342 &response,
1343 &RequestedRequiredStates::default(),
1344 &client.state_store_lock().lock().await,
1345 )
1346 .await
1347 .expect("Failed to process sync");
1348
1349 let room = client.get_room(room_id).expect("No room found");
1350 assert_eq!(room.cached_display_name().unwrap().to_string(), "Hello World");
1351
1352 assert_matches!(
1353 room_info_notable_update.recv().await,
1354 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons }) => {
1355 assert_eq!(received_room_id, room_id);
1356 assert!(reasons.contains(RoomInfoNotableUpdateReasons::NONE));
1357 }
1358 );
1359 assert_matches!(
1360 room_info_notable_update.recv().await,
1361 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons }) => {
1362 assert_eq!(received_room_id, room_id);
1363 assert!(reasons.contains(RoomInfoNotableUpdateReasons::DISPLAY_NAME));
1365 }
1366 );
1367 assert!(room_info_notable_update.is_empty());
1368 }
1369
1370 #[async_test]
1371 async fn test_display_name_is_persisted_from_sliding_sync() {
1372 let user_id = user_id!("@u:e.uk");
1373 let room_id = room_id!("!r:e.uk");
1374 let session_meta = SessionMeta { user_id: user_id.to_owned(), device_id: "FOOBAR".into() };
1375 let state_store;
1376
1377 {
1378 let client = {
1379 let store = StoreConfig::new(CrossProcessLockConfig::SingleProcess);
1380 state_store = store.state_store.clone();
1381
1382 let client =
1383 BaseClient::new(store, ThreadingSupport::Disabled, DmRoomDefinition::default());
1384 client
1385 .activate(
1386 session_meta.clone(),
1387 RoomLoadSettings::default(),
1388 #[cfg(feature = "e2e-encryption")]
1389 None,
1390 )
1391 .await
1392 .expect("`activate` failed!");
1393
1394 client
1395 };
1396
1397 let room = room_with_name("Hello World", user_id);
1400 let response = response_with_room(room_id, room);
1401 client
1402 .process_sliding_sync(
1403 &response,
1404 &RequestedRequiredStates::default(),
1405 &client.state_store_lock().lock().await,
1406 )
1407 .await
1408 .expect("Failed to process sync");
1409
1410 let room = client.get_room(room_id).expect("No room found");
1411 assert_eq!(room.cached_display_name().unwrap().to_string(), "Hello World");
1412 }
1413
1414 {
1415 let client = {
1416 let mut store = StoreConfig::new(CrossProcessLockConfig::SingleProcess);
1417 store.state_store = state_store;
1418 let client =
1419 BaseClient::new(store, ThreadingSupport::Disabled, DmRoomDefinition::default());
1420 client
1421 .activate(
1422 session_meta,
1423 RoomLoadSettings::default(),
1424 #[cfg(feature = "e2e-encryption")]
1425 None,
1426 )
1427 .await
1428 .expect("`activate` failed!");
1429
1430 client
1431 };
1432
1433 let room = client.get_room(room_id).expect("No room found");
1434 assert_eq!(room.cached_display_name().unwrap().to_string(), "Hello World");
1435 }
1436 }
1437
1438 #[async_test]
1439 async fn test_compute_heroes_from_sliding_sync() {
1440 let client = logged_in_base_client(None).await;
1442 let room_id = room_id!("!r:e.uk");
1443 let gordon = owned_user_id!("@gordon:e.uk");
1444 let alice = owned_user_id!("@alice:e.uk");
1445
1446 let mut room = http::response::Room::new();
1449 room.heroes = Some(vec![
1450 assign!(http::response::Hero::new(gordon), {
1451 name: Some("Gordon".to_owned()),
1452 }),
1453 assign!(http::response::Hero::new(alice), {
1454 name: Some("Alice".to_owned()),
1455 avatar: Some(owned_mxc_uri!("mxc://e.uk/med1"))
1456 }),
1457 ]);
1458 let response = response_with_room(room_id, room);
1459 let _sync_resp = client
1460 .process_sliding_sync(
1461 &response,
1462 &RequestedRequiredStates::default(),
1463 &client.state_store_lock().lock().await,
1464 )
1465 .await
1466 .expect("Failed to process sync");
1467
1468 let client_room = client.get_room(room_id).expect("No room found");
1470 assert_eq!(client_room.room_id(), room_id);
1471 assert_eq!(client_room.state(), RoomState::Joined);
1472
1473 assert_eq!(
1475 client_room.clone_info().summary.heroes(),
1476 &[
1477 RoomHero {
1478 user_id: owned_user_id!("@gordon:e.uk"),
1479 display_name: Some("Gordon".to_owned()),
1480 avatar_url: None
1481 },
1482 RoomHero {
1483 user_id: owned_user_id!("@alice:e.uk"),
1484 display_name: Some("Alice".to_owned()),
1485 avatar_url: Some(owned_mxc_uri!("mxc://e.uk/med1"))
1486 },
1487 ]
1488 );
1489 }
1490
1491 #[async_test]
1492 async fn test_recency_stamp_is_found_when_processing_sliding_sync_response() {
1493 let client = logged_in_base_client(None).await;
1495 let room_id = room_id!("!r:e.uk");
1496
1497 let room = assign!(http::response::Room::new(), {
1499 bump_stamp: Some(42u32.into()),
1500 });
1501 let response = response_with_room(room_id, room);
1502 client
1503 .process_sliding_sync(
1504 &response,
1505 &RequestedRequiredStates::default(),
1506 &client.state_store_lock().lock().await,
1507 )
1508 .await
1509 .expect("Failed to process sync");
1510
1511 let client_room = client.get_room(room_id).expect("No room found");
1513 assert_eq!(client_room.recency_stamp().expect("No recency stamp"), 42.into());
1514 }
1515
1516 #[async_test]
1517 async fn test_recency_stamp_can_be_overwritten_when_present_in_a_sliding_sync_response() {
1518 let client = logged_in_base_client(None).await;
1520 let room_id = room_id!("!r:e.uk");
1521
1522 {
1523 let room = assign!(http::response::Room::new(), {
1525 bump_stamp: Some(42u32.into()),
1526 });
1527 let response = response_with_room(room_id, room);
1528 client
1529 .process_sliding_sync(
1530 &response,
1531 &RequestedRequiredStates::default(),
1532 &client.state_store_lock().lock().await,
1533 )
1534 .await
1535 .expect("Failed to process sync");
1536
1537 let client_room = client.get_room(room_id).expect("No room found");
1539 assert_eq!(client_room.recency_stamp().expect("No recency stamp"), 42.into());
1540 }
1541
1542 {
1543 let room = assign!(http::response::Room::new(), {
1545 bump_stamp: None,
1546 });
1547 let response = response_with_room(room_id, room);
1548 client
1549 .process_sliding_sync(
1550 &response,
1551 &RequestedRequiredStates::default(),
1552 &client.state_store_lock().lock().await,
1553 )
1554 .await
1555 .expect("Failed to process sync");
1556
1557 let client_room = client.get_room(room_id).expect("No room found");
1559 assert_eq!(client_room.recency_stamp().expect("No recency stamp"), 42.into());
1560 }
1561
1562 {
1563 let room = assign!(http::response::Room::new(), {
1566 bump_stamp: Some(153u32.into()),
1567 });
1568 let response = response_with_room(room_id, room);
1569 client
1570 .process_sliding_sync(
1571 &response,
1572 &RequestedRequiredStates::default(),
1573 &client.state_store_lock().lock().await,
1574 )
1575 .await
1576 .expect("Failed to process sync");
1577
1578 let client_room = client.get_room(room_id).expect("No room found");
1580 assert_eq!(client_room.recency_stamp().expect("No recency stamp"), 153.into());
1581 }
1582 }
1583
1584 #[async_test]
1585 async fn test_recency_stamp_can_trigger_a_notable_update_reason() {
1586 let client = logged_in_base_client(None).await;
1588 let mut room_info_notable_update_stream = client.room_info_notable_update_receiver();
1589 let room_id = room_id!("!r:e.uk");
1590
1591 let room = assign!(http::response::Room::new(), {
1593 bump_stamp: Some(42u32.into()),
1594 });
1595 let response = response_with_room(room_id, room);
1596 client
1597 .process_sliding_sync(
1598 &response,
1599 &RequestedRequiredStates::default(),
1600 &client.state_store_lock().lock().await,
1601 )
1602 .await
1603 .expect("Failed to process sync");
1604
1605 assert_matches!(
1608 room_info_notable_update_stream.recv().await,
1609 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1610 assert_eq!(received_room_id, room_id);
1611 assert!(!received_reasons.contains(RoomInfoNotableUpdateReasons::RECENCY_STAMP));
1612 }
1613 );
1614 assert_matches!(
1615 room_info_notable_update_stream.recv().await,
1616 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1617 assert_eq!(received_room_id, room_id);
1618 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::DISPLAY_NAME));
1619 }
1620 );
1621 assert!(room_info_notable_update_stream.is_empty());
1622
1623 let room = assign!(http::response::Room::new(), {
1625 bump_stamp: Some(43u32.into()),
1626 });
1627 let response = response_with_room(room_id, room);
1628 client
1629 .process_sliding_sync(
1630 &response,
1631 &RequestedRequiredStates::default(),
1632 &client.state_store_lock().lock().await,
1633 )
1634 .await
1635 .expect("Failed to process sync");
1636
1637 assert_matches!(
1639 room_info_notable_update_stream.recv().await,
1640 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1641 assert_eq!(received_room_id, room_id);
1642 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::RECENCY_STAMP));
1643 }
1644 );
1645 assert!(room_info_notable_update_stream.is_empty());
1646 }
1647
1648 #[async_test]
1649 async fn test_leaving_room_can_trigger_a_notable_update_reason() {
1650 let client = logged_in_base_client(None).await;
1652 let mut room_info_notable_update_stream = client.room_info_notable_update_receiver();
1653
1654 let room_id = room_id!("!r:e.uk");
1656 let room = http::response::Room::new();
1657 let response = response_with_room(room_id, room);
1658 client
1659 .process_sliding_sync(
1660 &response,
1661 &RequestedRequiredStates::default(),
1662 &client.state_store_lock().lock().await,
1663 )
1664 .await
1665 .expect("Failed to process sync");
1666
1667 assert_matches!(
1669 room_info_notable_update_stream.recv().await,
1670 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1671 assert_eq!(received_room_id, room_id);
1672 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::NONE));
1673 }
1674 );
1675 assert_matches!(
1676 room_info_notable_update_stream.recv().await,
1677 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1678 assert_eq!(received_room_id, room_id);
1679 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::DISPLAY_NAME));
1680 }
1681 );
1682
1683 let room_id = room_id!("!r:e.uk");
1685 let events = vec![
1686 Raw::from_json_string(
1687 json!({
1688 "type": "m.room.member",
1689 "event_id": "$3",
1690 "content": { "membership": "join" },
1691 "sender": "@u:h.uk",
1692 "origin_server_ts": 12344445,
1693 "state_key": "@u:e.uk",
1694 })
1695 .to_string(),
1696 )
1697 .unwrap(),
1698 ];
1699 let room = assign!(http::response::Room::new(), {
1700 required_state: events,
1701 });
1702 let response = response_with_room(room_id, room);
1703 client
1704 .process_sliding_sync(
1705 &response,
1706 &RequestedRequiredStates::default(),
1707 &client.state_store_lock().lock().await,
1708 )
1709 .await
1710 .expect("Failed to process sync");
1711
1712 assert_matches!(
1714 room_info_notable_update_stream.recv().await,
1715 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1716 assert_eq!(received_room_id, room_id);
1717 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::NONE));
1718 }
1719 );
1720 assert!(room_info_notable_update_stream.is_empty());
1721
1722 let events = vec![
1723 Raw::from_json_string(
1724 json!({
1725 "type": "m.room.member",
1726 "event_id": "$3",
1727 "content": { "membership": "leave" },
1728 "sender": "@u:h.uk",
1729 "origin_server_ts": 12344445,
1730 "state_key": "@u:e.uk",
1731 })
1732 .to_string(),
1733 )
1734 .unwrap(),
1735 ];
1736 let room = assign!(http::response::Room::new(), {
1737 required_state: events,
1738 });
1739 let response = response_with_room(room_id, room);
1740 client
1741 .process_sliding_sync(
1742 &response,
1743 &RequestedRequiredStates::default(),
1744 &client.state_store_lock().lock().await,
1745 )
1746 .await
1747 .expect("Failed to process sync");
1748
1749 assert_matches!(
1751 room_info_notable_update_stream.recv().await,
1752 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1753 assert_eq!(received_room_id, room_id);
1754 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::MEMBERSHIP));
1755 }
1756 );
1757 assert!(room_info_notable_update_stream.is_empty());
1758 }
1759
1760 #[async_test]
1761 async fn test_unread_marker_can_trigger_a_notable_update_reason() {
1762 let client = logged_in_base_client(None).await;
1764 let mut room_info_notable_update_stream = client.room_info_notable_update_receiver();
1765
1766 let room_id = room_id!("!r:e.uk");
1768 let room = http::response::Room::new();
1769 let response = response_with_room(room_id, room);
1770 client
1771 .process_sliding_sync(
1772 &response,
1773 &RequestedRequiredStates::default(),
1774 &client.state_store_lock().lock().await,
1775 )
1776 .await
1777 .expect("Failed to process sync");
1778
1779 assert_matches!(
1781 room_info_notable_update_stream.recv().await,
1782 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1783 assert_eq!(received_room_id, room_id);
1784 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::NONE), "{received_reasons:?}");
1785 }
1786 );
1787 assert_matches!(
1788 room_info_notable_update_stream.recv().await,
1789 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1790 assert_eq!(received_room_id, room_id);
1791 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::DISPLAY_NAME), "{received_reasons:?}");
1792 }
1793 );
1794 assert!(room_info_notable_update_stream.is_empty());
1795
1796 let room_id = room_id!("!r:e.uk");
1799 let room_account_data_events = vec![
1800 Raw::from_json_string(
1801 json!({
1802 "type": "m.marked_unread",
1803 "event_id": "$1",
1804 "content": { "unread": true },
1805 "sender": client.session_meta().unwrap().user_id,
1806 "origin_server_ts": 12344445,
1807 })
1808 .to_string(),
1809 )
1810 .unwrap(),
1811 ];
1812 let mut response = response_with_room(room_id, http::response::Room::new());
1813 response.extensions.account_data.rooms.insert(room_id.to_owned(), room_account_data_events);
1814
1815 client
1816 .process_sliding_sync(
1817 &response,
1818 &RequestedRequiredStates::default(),
1819 &client.state_store_lock().lock().await,
1820 )
1821 .await
1822 .expect("Failed to process sync");
1823
1824 assert_matches!(
1826 room_info_notable_update_stream.recv().await,
1827 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1828 assert_eq!(received_room_id, room_id);
1829 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::UNREAD_MARKER), "{received_reasons:?}");
1830 }
1831 );
1832
1833 client
1835 .process_sliding_sync(
1836 &response,
1837 &RequestedRequiredStates::default(),
1838 &client.state_store_lock().lock().await,
1839 )
1840 .await
1841 .expect("Failed to process sync");
1842
1843 assert_matches!(
1844 room_info_notable_update_stream.recv().await,
1845 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1846 assert_eq!(received_room_id, room_id);
1847 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::NONE), "{received_reasons:?}");
1848 }
1849 );
1850 assert!(room_info_notable_update_stream.is_empty());
1851
1852 let room_account_data_events = vec![
1854 Raw::from_json_string(
1855 json!({
1856 "type": "m.marked_unread",
1857 "event_id": "$1",
1858 "content": { "unread": false },
1859 "sender": client.session_meta().unwrap().user_id,
1860 "origin_server_ts": 12344445,
1861 })
1862 .to_string(),
1863 )
1864 .unwrap(),
1865 ];
1866 response.extensions.account_data.rooms.insert(room_id.to_owned(), room_account_data_events);
1867 client
1868 .process_sliding_sync(
1869 &response,
1870 &RequestedRequiredStates::default(),
1871 &client.state_store_lock().lock().await,
1872 )
1873 .await
1874 .expect("Failed to process sync");
1875
1876 assert_matches!(
1877 room_info_notable_update_stream.recv().await,
1878 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1879 assert_eq!(received_room_id, room_id);
1880 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::UNREAD_MARKER));
1881 }
1882 );
1883 assert!(room_info_notable_update_stream.is_empty());
1884 }
1885
1886 #[async_test]
1887 async fn test_unstable_unread_marker_is_ignored_after_stable() {
1888 let client = logged_in_base_client(None).await;
1890 let mut room_info_notable_update_stream = client.room_info_notable_update_receiver();
1891
1892 let room_id = room_id!("!r:e.uk");
1894 let room = http::response::Room::new();
1895 let response = response_with_room(room_id, room);
1896 client
1897 .process_sliding_sync(
1898 &response,
1899 &RequestedRequiredStates::default(),
1900 &client.state_store_lock().lock().await,
1901 )
1902 .await
1903 .expect("Failed to process sync");
1904
1905 assert_matches!(
1907 room_info_notable_update_stream.recv().await,
1908 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1909 assert_eq!(received_room_id, room_id);
1910 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::NONE), "{received_reasons:?}");
1911 }
1912 );
1913 assert_matches!(
1914 room_info_notable_update_stream.recv().await,
1915 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1916 assert_eq!(received_room_id, room_id);
1917 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::DISPLAY_NAME), "{received_reasons:?}");
1918 }
1919 );
1920 assert!(room_info_notable_update_stream.is_empty());
1921
1922 let room_id = room_id!("!r:e.uk");
1925 let unstable_room_account_data_events = vec![
1926 Raw::from_json_string(
1927 json!({
1928 "type": "com.famedly.marked_unread",
1929 "event_id": "$1",
1930 "content": { "unread": true },
1931 "sender": client.session_meta().unwrap().user_id,
1932 "origin_server_ts": 12344445,
1933 })
1934 .to_string(),
1935 )
1936 .unwrap(),
1937 ];
1938 let mut response = response_with_room(room_id, http::response::Room::new());
1939 response
1940 .extensions
1941 .account_data
1942 .rooms
1943 .insert(room_id.to_owned(), unstable_room_account_data_events.clone());
1944
1945 client
1946 .process_sliding_sync(
1947 &response,
1948 &RequestedRequiredStates::default(),
1949 &client.state_store_lock().lock().await,
1950 )
1951 .await
1952 .expect("Failed to process sync");
1953
1954 assert_matches!(
1956 room_info_notable_update_stream.recv().await,
1957 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1958 assert_eq!(received_room_id, room_id);
1959 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::UNREAD_MARKER), "{received_reasons:?}");
1960 }
1961 );
1962 assert!(room_info_notable_update_stream.is_empty());
1963
1964 let stable_room_account_data_events = vec![
1966 Raw::from_json_string(
1967 json!({
1968 "type": "m.marked_unread",
1969 "event_id": "$1",
1970 "content": { "unread": false },
1971 "sender": client.session_meta().unwrap().user_id,
1972 "origin_server_ts": 12344445,
1973 })
1974 .to_string(),
1975 )
1976 .unwrap(),
1977 ];
1978 response
1979 .extensions
1980 .account_data
1981 .rooms
1982 .insert(room_id.to_owned(), stable_room_account_data_events);
1983 client
1984 .process_sliding_sync(
1985 &response,
1986 &RequestedRequiredStates::default(),
1987 &client.state_store_lock().lock().await,
1988 )
1989 .await
1990 .expect("Failed to process sync");
1991
1992 assert_matches!(
1994 room_info_notable_update_stream.recv().await,
1995 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1996 assert_eq!(received_room_id, room_id);
1997 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::UNREAD_MARKER));
1998 }
1999 );
2000 assert!(room_info_notable_update_stream.is_empty());
2001
2002 response
2005 .extensions
2006 .account_data
2007 .rooms
2008 .insert(room_id.to_owned(), unstable_room_account_data_events);
2009 client
2010 .process_sliding_sync(
2011 &response,
2012 &RequestedRequiredStates::default(),
2013 &client.state_store_lock().lock().await,
2014 )
2015 .await
2016 .expect("Failed to process sync");
2017
2018 assert_matches!(
2020 room_info_notable_update_stream.recv().await,
2021 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
2022 assert_eq!(received_room_id, room_id);
2023 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::NONE), "{received_reasons:?}");
2024 }
2025 );
2026 assert!(room_info_notable_update_stream.is_empty());
2027
2028 let stable_room_account_data_events = vec![
2031 Raw::from_json_string(
2032 json!({
2033 "type": "m.marked_unread",
2034 "event_id": "$3",
2035 "content": { "unread": true },
2036 "sender": client.session_meta().unwrap().user_id,
2037 "origin_server_ts": 12344445,
2038 })
2039 .to_string(),
2040 )
2041 .unwrap(),
2042 ];
2043 response
2044 .extensions
2045 .account_data
2046 .rooms
2047 .insert(room_id.to_owned(), stable_room_account_data_events);
2048 client
2049 .process_sliding_sync(
2050 &response,
2051 &RequestedRequiredStates::default(),
2052 &client.state_store_lock().lock().await,
2053 )
2054 .await
2055 .expect("Failed to process sync");
2056
2057 assert_matches!(
2059 room_info_notable_update_stream.recv().await,
2060 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
2061 assert_eq!(received_room_id, room_id);
2062 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::UNREAD_MARKER));
2063 }
2064 );
2065 assert!(room_info_notable_update_stream.is_empty());
2066 }
2067
2068 #[async_test]
2069 async fn test_pinned_events_are_updated_on_sync() {
2070 let user_a_id = user_id!("@a:e.uk");
2071 let client = logged_in_base_client(Some(user_a_id)).await;
2072 let room_id = room_id!("!r:e.uk");
2073 let pinned_event_id = owned_event_id!("$an-id:e.uk");
2074
2075 let mut room_response = http::response::Room::new();
2077 set_room_joined(&mut room_response, user_a_id);
2078 let response = response_with_room(room_id, room_response);
2079 client
2080 .process_sliding_sync(
2081 &response,
2082 &RequestedRequiredStates::default(),
2083 &client.state_store_lock().lock().await,
2084 )
2085 .await
2086 .expect("Failed to process sync");
2087
2088 let room = client.get_room(room_id).unwrap();
2090 let pinned_event_ids = room.pinned_event_ids();
2091 assert_matches!(pinned_event_ids, None);
2092
2093 let mut room_response = http::response::Room::new();
2095 room_response.required_state.push(make_state_event(
2096 user_a_id,
2097 "",
2098 RoomPinnedEventsEventContent::new(vec![pinned_event_id.clone()]),
2099 None,
2100 ));
2101 let response = response_with_room(room_id, room_response);
2102 client
2103 .process_sliding_sync(
2104 &response,
2105 &RequestedRequiredStates::default(),
2106 &client.state_store_lock().lock().await,
2107 )
2108 .await
2109 .expect("Failed to process sync");
2110
2111 let pinned_event_ids = room.pinned_event_ids().unwrap_or_default();
2112 assert_eq!(pinned_event_ids.len(), 1);
2113 assert_eq!(pinned_event_ids[0], pinned_event_id);
2114
2115 let mut room_response = http::response::Room::new();
2117 room_response.required_state.push(make_state_event(
2118 user_a_id,
2119 "",
2120 RoomPinnedEventsEventContent::new(Vec::new()),
2121 None,
2122 ));
2123 let response = response_with_room(room_id, room_response);
2124 client
2125 .process_sliding_sync(
2126 &response,
2127 &RequestedRequiredStates::default(),
2128 &client.state_store_lock().lock().await,
2129 )
2130 .await
2131 .expect("Failed to process sync");
2132 let pinned_event_ids = room.pinned_event_ids().unwrap();
2133 assert!(pinned_event_ids.is_empty());
2134 }
2135
2136 #[async_test]
2137 async fn test_dms_are_processed_in_any_sync_response() {
2138 let current_user_id = user_id!("@current:e.uk");
2139 let client = logged_in_base_client(Some(current_user_id)).await;
2140 let user_a_id = user_id!("@a:e.uk");
2141 let user_b_id = user_id!("@b:e.uk");
2142 let room_id_1 = room_id!("!r:e.uk");
2143 let room_id_2 = room_id!("!s:e.uk");
2144
2145 let mut room_response = http::response::Room::new();
2146 set_room_joined(&mut room_response, user_a_id);
2147 let mut response = response_with_room(room_id_1, room_response);
2148 let mut direct_content: BTreeMap<OwnedDirectUserIdentifier, Vec<OwnedRoomId>> =
2149 BTreeMap::new();
2150 direct_content.insert(user_a_id.into(), vec![room_id_1.to_owned()]);
2151 direct_content.insert(user_b_id.into(), vec![room_id_2.to_owned()]);
2152 response
2153 .extensions
2154 .account_data
2155 .global
2156 .push(make_global_account_data_event(DirectEventContent(direct_content)));
2157 client
2158 .process_sliding_sync(
2159 &response,
2160 &RequestedRequiredStates::default(),
2161 &client.state_store_lock().lock().await,
2162 )
2163 .await
2164 .expect("Failed to process sync");
2165
2166 let room_1 = client.get_room(room_id_1).unwrap();
2167 assert!(room_1.is_direct().await.unwrap());
2168
2169 let mut room_response = http::response::Room::new();
2171 set_room_joined(&mut room_response, user_b_id);
2172 let response = response_with_room(room_id_2, room_response);
2173 client
2174 .process_sliding_sync(
2175 &response,
2176 &RequestedRequiredStates::default(),
2177 &client.state_store_lock().lock().await,
2178 )
2179 .await
2180 .expect("Failed to process sync");
2181
2182 let room_2 = client.get_room(room_id_2).unwrap();
2183 assert!(room_2.is_direct().await.unwrap());
2184 }
2185
2186 #[async_test]
2187 async fn test_room_encryption_state_is_and_is_not_encrypted() {
2188 let user_id = user_id!("@raclette:patate");
2189 let client = logged_in_base_client(Some(user_id)).await;
2190 let room_id_0 = room_id!("!r0");
2191 let room_id_1 = room_id!("!r1");
2192 let room_id_2 = room_id!("!r2");
2193
2194 let requested_required_states = RequestedRequiredStates::from(&{
2211 let mut request = http::Request::new();
2212
2213 request.room_subscriptions.insert(room_id_0.to_owned(), {
2214 let mut room_subscription = http::request::RoomSubscription::default();
2215
2216 room_subscription
2217 .required_state
2218 .push((StateEventType::RoomEncryption, "".to_owned()));
2219
2220 room_subscription
2221 });
2222
2223 request
2224 });
2225
2226 let mut response = http::Response::new("0".to_owned());
2227
2228 {
2232 let not_encrypted_room = http::response::Room::new();
2233 let mut encrypted_room = http::response::Room::new();
2234 set_room_is_encrypted(&mut encrypted_room, user_id);
2235
2236 response.rooms.insert(room_id_0.to_owned(), encrypted_room.clone());
2237 response.rooms.insert(room_id_1.to_owned(), encrypted_room);
2238 response.rooms.insert(room_id_2.to_owned(), not_encrypted_room);
2239 }
2240
2241 client
2242 .process_sliding_sync(
2243 &response,
2244 &requested_required_states,
2245 &client.state_store_lock().lock().await,
2246 )
2247 .await
2248 .expect("Failed to process sync");
2249
2250 assert_matches!(
2252 client.get_room(room_id_0).unwrap().encryption_state(),
2253 EncryptionState::Encrypted
2254 );
2255 assert_matches!(
2256 client.get_room(room_id_1).unwrap().encryption_state(),
2257 EncryptionState::Encrypted
2258 );
2259 assert_matches!(
2261 client.get_room(room_id_2).unwrap().encryption_state(),
2262 EncryptionState::NotEncrypted
2263 )
2264 }
2265
2266 #[async_test]
2267 async fn test_room_encryption_state_is_unknown() {
2268 let user_id = user_id!("@raclette:patate");
2269 let client = logged_in_base_client(Some(user_id)).await;
2270 let room_id_0 = room_id!("!r0");
2271 let room_id_1 = room_id!("!r1");
2272
2273 let requested_required_states = RequestedRequiredStates::from(&http::Request::new());
2286
2287 let mut response = http::Response::new("0".to_owned());
2288
2289 {
2291 let not_encrypted_room = http::response::Room::new();
2292 let mut encrypted_room = http::response::Room::new();
2293 set_room_is_encrypted(&mut encrypted_room, user_id);
2294
2295 response.rooms.insert(room_id_0.to_owned(), encrypted_room);
2296 response.rooms.insert(room_id_1.to_owned(), not_encrypted_room);
2297 }
2298
2299 client
2300 .process_sliding_sync(
2301 &response,
2302 &requested_required_states,
2303 &client.state_store_lock().lock().await,
2304 )
2305 .await
2306 .expect("Failed to process sync");
2307
2308 assert_matches!(
2311 client.get_room(room_id_0).unwrap().encryption_state(),
2312 EncryptionState::Encrypted
2313 );
2314 assert_matches!(
2317 client.get_room(room_id_1).unwrap().encryption_state(),
2318 EncryptionState::Unknown
2319 );
2320 }
2321
2322 async fn membership(
2323 client: &BaseClient,
2324 room_id: &RoomId,
2325 user_id: &UserId,
2326 ) -> MembershipState {
2327 let room = client.get_room(room_id).expect("Room not found!");
2328 let member = room.get_member(user_id).await.unwrap().expect("B not in room");
2329 member.membership().clone()
2330 }
2331
2332 fn direct_targets(client: &BaseClient, room_id: &RoomId) -> HashSet<OwnedDirectUserIdentifier> {
2333 let room = client.get_room(room_id).expect("Room not found!");
2334 room.direct_targets()
2335 }
2336
2337 async fn create_dm(
2340 client: &BaseClient,
2341 room_id: &RoomId,
2342 my_id: &UserId,
2343 their_id: &UserId,
2344 other_state: MembershipState,
2345 ) {
2346 let mut room = http::response::Room::new();
2347 set_room_joined(&mut room, my_id);
2348
2349 match other_state {
2350 MembershipState::Join => {
2351 room.joined_count = Some(uint!(2));
2352 room.invited_count = None;
2353 }
2354
2355 MembershipState::Invite => {
2356 room.joined_count = Some(uint!(1));
2357 room.invited_count = Some(uint!(1));
2358 }
2359
2360 _ => {
2361 room.joined_count = Some(uint!(1));
2362 room.invited_count = None;
2363 }
2364 }
2365
2366 room.required_state.push(make_membership_event(their_id, other_state));
2367
2368 let mut response = response_with_room(room_id, room);
2369 set_direct_with(&mut response, their_id.to_owned(), vec![room_id.to_owned()]);
2370 client
2371 .process_sliding_sync(
2372 &response,
2373 &RequestedRequiredStates::default(),
2374 &client.state_store_lock().lock().await,
2375 )
2376 .await
2377 .expect("Failed to process sync");
2378 }
2379
2380 async fn update_room_membership(
2382 client: &BaseClient,
2383 room_id: &RoomId,
2384 user_id: &UserId,
2385 new_state: MembershipState,
2386 ) {
2387 let mut room = http::response::Room::new();
2388 room.required_state.push(make_membership_event(user_id, new_state));
2389 let response = response_with_room(room_id, room);
2390 client
2391 .process_sliding_sync(
2392 &response,
2393 &RequestedRequiredStates::default(),
2394 &client.state_store_lock().lock().await,
2395 )
2396 .await
2397 .expect("Failed to process sync");
2398 }
2399
2400 fn set_direct_with(
2401 response: &mut http::Response,
2402 user_id: OwnedUserId,
2403 room_ids: Vec<OwnedRoomId>,
2404 ) {
2405 let mut direct_content: BTreeMap<OwnedDirectUserIdentifier, Vec<OwnedRoomId>> =
2406 BTreeMap::new();
2407 direct_content.insert(user_id.into(), room_ids);
2408 response
2409 .extensions
2410 .account_data
2411 .global
2412 .push(make_global_account_data_event(DirectEventContent(direct_content)));
2413 }
2414
2415 fn response_with_room(room_id: &RoomId, room: http::response::Room) -> http::Response {
2416 let mut response = http::Response::new("5".to_owned());
2417 response.rooms.insert(room_id.to_owned(), room);
2418 response
2419 }
2420
2421 fn room_with_avatar(avatar_uri: &MxcUri, user_id: &UserId) -> http::response::Room {
2422 let mut room = http::response::Room::new();
2423
2424 let mut avatar_event_content = RoomAvatarEventContent::new();
2425 avatar_event_content.url = Some(avatar_uri.to_owned());
2426
2427 room.required_state.push(make_state_event(user_id, "", avatar_event_content, None));
2428
2429 room
2430 }
2431
2432 fn room_with_canonical_alias(
2433 room_alias_id: &RoomAliasId,
2434 user_id: &UserId,
2435 ) -> http::response::Room {
2436 let mut room = http::response::Room::new();
2437
2438 let mut canonical_alias_event_content = RoomCanonicalAliasEventContent::new();
2439 canonical_alias_event_content.alias = Some(room_alias_id.to_owned());
2440
2441 room.required_state.push(make_state_event(
2442 user_id,
2443 "",
2444 canonical_alias_event_content,
2445 None,
2446 ));
2447
2448 room
2449 }
2450
2451 fn room_with_name(name: &str, user_id: &UserId) -> http::response::Room {
2452 let mut room = http::response::Room::new();
2453
2454 let name_event_content = RoomNameEventContent::new(name.to_owned());
2455
2456 room.required_state.push(make_state_event(user_id, "", name_event_content, None));
2457
2458 room
2459 }
2460
2461 fn set_room_name(room: &mut http::response::Room, sender: &UserId, name: String) {
2462 room.required_state.push(make_state_event(
2463 sender,
2464 "",
2465 RoomNameEventContent::new(name),
2466 None,
2467 ));
2468 }
2469
2470 fn set_room_invited(room: &mut http::response::Room, inviter: &UserId, invitee: &UserId) {
2471 let evt = Raw::new(&json!({
2475 "type": "m.room.member",
2476 "sender": inviter,
2477 "content": {
2478 "is_direct": true,
2479 "membership": "invite",
2480 },
2481 "state_key": invitee,
2482 }))
2483 .expect("Failed to make raw event")
2484 .cast_unchecked();
2485
2486 room.invite_state = Some(vec![evt]);
2487
2488 room.required_state.push(make_state_event(
2491 inviter,
2492 invitee.as_str(),
2493 RoomMemberEventContent::new(MembershipState::Invite),
2494 None,
2495 ));
2496 }
2497
2498 fn set_room_knocked(room: &mut http::response::Room, knocker: &UserId) {
2499 let evt = Raw::new(&json!({
2503 "type": "m.room.member",
2504 "sender": knocker,
2505 "content": {
2506 "is_direct": true,
2507 "membership": "knock",
2508 },
2509 "state_key": knocker,
2510 }))
2511 .expect("Failed to make raw event")
2512 .cast_unchecked();
2513
2514 room.invite_state = Some(vec![evt]);
2515 }
2516
2517 fn set_room_joined(room: &mut http::response::Room, user_id: &UserId) {
2518 room.required_state.push(make_membership_event(user_id, MembershipState::Join));
2519 }
2520
2521 fn set_room_left(room: &mut http::response::Room, user_id: &UserId) {
2522 room.required_state.push(make_membership_event(user_id, MembershipState::Leave));
2523 }
2524
2525 fn set_room_left_as_timeline_event(room: &mut http::response::Room, user_id: &UserId) {
2526 room.timeline.push(make_membership_event(user_id, MembershipState::Leave));
2527 }
2528
2529 fn set_room_is_encrypted(room: &mut http::response::Room, user_id: &UserId) {
2530 room.required_state.push(make_encryption_event(user_id));
2531 }
2532
2533 fn make_membership_event<K>(user_id: &UserId, state: MembershipState) -> Raw<K> {
2534 make_state_event(user_id, user_id.as_str(), RoomMemberEventContent::new(state), None)
2535 }
2536
2537 fn make_encryption_event<K>(user_id: &UserId) -> Raw<K> {
2538 make_state_event(user_id, "", RoomEncryptionEventContent::with_recommended_defaults(), None)
2539 }
2540
2541 fn make_global_account_data_event<C: GlobalAccountDataEventContent, E>(content: C) -> Raw<E> {
2542 Raw::new(&json!({
2543 "type": content.event_type(),
2544 "content": content,
2545 }))
2546 .expect("Failed to create account data event")
2547 .cast_unchecked()
2548 }
2549
2550 fn make_state_event<C: StateEventContent, E>(
2551 sender: &UserId,
2552 state_key: &str,
2553 content: C,
2554 prev_content: Option<C>,
2555 ) -> Raw<E> {
2556 let unsigned = if let Some(prev_content) = prev_content {
2557 json!({ "prev_content": prev_content })
2558 } else {
2559 json!({})
2560 };
2561
2562 Raw::new(&json!({
2563 "type": content.event_type(),
2564 "state_key": state_key,
2565 "content": content,
2566 "event_id": event_id!("$evt"),
2567 "sender": sender,
2568 "origin_server_ts": 10,
2569 "unsigned": unsigned,
2570 }))
2571 .expect("Failed to create state event")
2572 .cast_unchecked()
2573 }
2574}