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 tracing::{instrument, trace};
25
26use super::BaseClient;
27use crate::{
28 RequestedRequiredStates,
29 error::Result,
30 response_processors as processors,
31 store::ambiguity_map::AmbiguityCache,
32 sync::{RoomUpdates, SyncResponse},
33};
34
35impl BaseClient {
36 #[cfg(feature = "e2e-encryption")]
44 pub async fn process_sliding_sync_e2ee(
45 &self,
46 to_device: Option<&http::response::ToDevice>,
47 e2ee: &http::response::E2EE,
48 ) -> Result<Option<Vec<ProcessedToDeviceEvent>>> {
49 if to_device.is_none() && e2ee.is_empty() {
50 return Ok(None);
51 }
52
53 trace!(
54 to_device_events =
55 to_device.map(|to_device| to_device.events.len()).unwrap_or_default(),
56 device_one_time_keys_count = e2ee.device_one_time_keys_count.len(),
57 device_unused_fallback_key_types =
58 e2ee.device_unused_fallback_key_types.as_ref().map(|v| v.len()),
59 "Processing sliding sync e2ee events",
60 );
61
62 let olm_machine = self.olm_machine().await;
63
64 let context = processors::Context::default();
65
66 let processors::e2ee::to_device::Output { processed_to_device_events } =
67 processors::e2ee::to_device::from_msc4186(
68 to_device,
69 e2ee,
70 olm_machine.as_ref(),
71 &self.decryption_settings,
72 )
73 .await?;
74
75 processors::changes::save_and_apply(
76 context,
77 &self.state_store,
78 &self.ignore_user_list_changes,
79 None,
80 )
81 .await?;
82
83 Ok(Some(processed_to_device_events))
84 }
85
86 #[instrument(skip_all, level = "trace")]
93 pub async fn process_sliding_sync(
94 &self,
95 response: &http::Response,
96 requested_required_states: &RequestedRequiredStates,
97 ) -> Result<SyncResponse> {
98 let http::Response { rooms, lists, extensions, .. } = response;
99
100 trace!(
101 rooms = rooms.len(),
102 lists = lists.len(),
103 has_extensions = !extensions.is_empty(),
104 "Processing sliding sync room events"
105 );
106
107 if rooms.is_empty() && extensions.is_empty() {
108 return Ok(SyncResponse::default());
111 }
112
113 let _timer = timer!(tracing::Level::TRACE, "_method");
114
115 let mut context = processors::Context::default();
116
117 let state_store = self.state_store.clone();
118 let mut ambiguity_cache = AmbiguityCache::new(state_store.inner.clone());
119
120 let global_account_data_processor =
121 processors::account_data::global(&extensions.account_data.global);
122 let push_rules = self.get_push_rules(&global_account_data_processor).await?;
123
124 let mut room_updates = RoomUpdates::default();
125 let mut notifications = Default::default();
126
127 let user_id = self
128 .session_meta()
129 .expect("Sliding sync shouldn't run without an authenticated user")
130 .user_id
131 .to_owned();
132
133 for (room_id, room_response) in rooms {
134 let Some((room_info, room_update)) = processors::room::msc4186::update_any_room(
135 &mut context,
136 &user_id,
137 processors::room::RoomCreationData::new(
138 room_id,
139 requested_required_states,
140 &mut ambiguity_cache,
141 ),
142 room_response,
143 &extensions.account_data.rooms,
144 #[cfg(feature = "e2e-encryption")]
145 processors::e2ee::E2EE::new(
146 self.olm_machine().await.as_ref(),
147 &self.decryption_settings,
148 self.handle_verification_events,
149 ),
150 processors::notification::Notification::new(
151 &push_rules,
152 &mut notifications,
153 &self.state_store,
154 ),
155 )
156 .await?
157 else {
158 continue;
159 };
160
161 context.state_changes.add_room(room_info);
162
163 let room_id = room_id.to_owned();
164
165 use processors::room::msc4186::RoomUpdateKind;
166
167 match room_update {
168 RoomUpdateKind::Joined(joined_room_update) => {
169 room_updates.joined.insert(room_id, joined_room_update);
170 }
171 RoomUpdateKind::Left(left_room_update) => {
172 room_updates.left.insert(room_id, left_room_update);
173 }
174 RoomUpdateKind::Invited(invited_room_update) => {
175 room_updates.invited.insert(room_id, invited_room_update);
176 }
177 RoomUpdateKind::Knocked(knocked_room_update) => {
178 room_updates.knocked.insert(room_id, knocked_room_update);
179 }
180 }
181 }
182
183 processors::room::msc4186::extensions::dispatch_typing_ephemeral_events(
187 &extensions.typing,
188 &mut room_updates.joined,
189 );
190
191 processors::room::msc4186::extensions::room_account_data(
193 &mut context,
194 &extensions.account_data,
195 &mut room_updates,
196 &self.state_store,
197 );
198
199 global_account_data_processor.apply(&mut context, &state_store).await;
200
201 context.state_changes.ambiguity_maps = ambiguity_cache.cache;
202
203 processors::changes::save_and_apply(
205 context,
206 &self.state_store,
207 &self.ignore_user_list_changes,
208 None,
209 )
210 .await?;
211
212 let mut context = processors::Context::default();
213
214 processors::room::display_name::update_for_rooms(
217 &mut context,
218 &room_updates,
219 &self.state_store,
220 )
221 .await;
222
223 processors::changes::save_only(context, &self.state_store).await?;
225
226 Ok(SyncResponse {
227 rooms: room_updates,
228 notifications,
229 presence: Default::default(),
230 account_data: extensions.account_data.global.clone(),
231 to_device: Default::default(),
232 })
233 }
234
235 #[doc(hidden)]
238 pub async fn process_sliding_sync_receipts_extension_for_room(
239 &self,
240 room_id: &OwnedRoomId,
241 response: &http::Response,
242 ) -> Result<Option<Raw<SyncReceiptEvent>>> {
243 let mut context = processors::Context::default();
244
245 let mut save_context = false;
246
247 let receipt_ephemeral_event = if let Some(receipt_ephemeral_event) =
249 response.extensions.receipts.rooms.get(room_id)
250 {
251 processors::room::msc4186::extensions::dispatch_receipt_ephemeral_event_for_room(
252 &mut context,
253 room_id,
254 receipt_ephemeral_event,
255 );
256 save_context = true;
257 Some(receipt_ephemeral_event.clone())
258 } else {
259 None
260 };
261
262 if save_context {
264 processors::changes::save_only(context, &self.state_store).await?;
265 }
266
267 Ok(receipt_ephemeral_event)
268 }
269}
270
271#[cfg(all(test, not(target_family = "wasm")))]
272mod tests {
273 use std::collections::{BTreeMap, HashSet};
274
275 use assert_matches::assert_matches;
276 use matrix_sdk_test::async_test;
277 use ruma::{
278 JsOption, MxcUri, OwnedRoomId, OwnedUserId, RoomAliasId, RoomId, UserId,
279 api::client::sync::sync_events::UnreadNotificationsCount,
280 assign, event_id,
281 events::{
282 GlobalAccountDataEventContent, StateEventContent, StateEventType,
283 direct::{DirectEventContent, DirectUserIdentifier, OwnedDirectUserIdentifier},
284 room::{
285 avatar::RoomAvatarEventContent,
286 canonical_alias::RoomCanonicalAliasEventContent,
287 encryption::RoomEncryptionEventContent,
288 member::{MembershipState, RoomMemberEventContent},
289 name::RoomNameEventContent,
290 pinned_events::RoomPinnedEventsEventContent,
291 },
292 },
293 mxc_uri, owned_event_id, owned_mxc_uri, owned_user_id, room_alias_id, room_id,
294 serde::Raw,
295 uint, user_id,
296 };
297 use serde_json::json;
298
299 use super::http;
300 use crate::{
301 BaseClient, EncryptionState, RequestedRequiredStates, RoomInfoNotableUpdate, RoomState,
302 SessionMeta,
303 client::ThreadingSupport,
304 room::{RoomHero, RoomInfoNotableUpdateReasons},
305 store::{RoomLoadSettings, StoreConfig},
306 test_utils::logged_in_base_client,
307 };
308
309 #[async_test]
310 async fn test_invited_state_without_update_emits_invited_room() {
311 let client = logged_in_base_client(None).await;
312 let room_id = room_id!("!invite:e.uk");
313 let user_id = client.session_meta().unwrap().user_id.to_owned();
314
315 let mut room = http::response::Room::new();
316 room.invite_state = Some(invite_state_for(&user_id, MembershipState::Invite));
317
318 let response = response_with_room(room_id, room);
319
320 let sync_resp = client
321 .process_sliding_sync(&response, &RequestedRequiredStates::default())
322 .await
323 .unwrap();
324
325 assert!(sync_resp.rooms.invited.contains_key(room_id));
326 }
327
328 use matrix_sdk_common::cross_process_lock::CrossProcessLockConfig;
329 use ruma::events::AnyStrippedStateEvent;
330
331 fn invite_state_for(
332 user_id: &UserId,
333 membership: MembershipState,
334 ) -> Vec<Raw<AnyStrippedStateEvent>> {
335 let content = RoomMemberEventContent::new(membership);
336
337 let raw: Raw<AnyStrippedStateEvent> = Raw::from_json_string(
338 serde_json::json!({
339 "type": "m.room.member",
340 "state_key": user_id,
341 "content": content,
342 })
343 .to_string(),
344 )
345 .unwrap();
346
347 vec![raw]
348 }
349
350 #[async_test]
351 async fn test_knocked_state_emits_invited_room() {
352 let client = logged_in_base_client(None).await;
353 let room_id = room_id!("!knock:e.uk");
354 let user_id = client.session_meta().unwrap().user_id.to_owned();
355
356 let mut room = http::response::Room::new();
357 room.invite_state = Some(invite_state_for(&user_id, MembershipState::Knock));
358
359 let response = response_with_room(room_id, room);
360
361 let sync_resp = client
362 .process_sliding_sync(&response, &RequestedRequiredStates::default())
363 .await
364 .unwrap();
365
366 assert!(sync_resp.rooms.invited.contains_key(room_id));
368 }
369
370 #[async_test]
371 async fn test_notification_count_set() {
372 let client = logged_in_base_client(None).await;
373
374 let mut response = http::Response::new("42".to_owned());
375 let room_id = room_id!("!room:example.org");
376 let count = assign!(UnreadNotificationsCount::default(), {
377 highlight_count: Some(uint!(13)),
378 notification_count: Some(uint!(37)),
379 });
380
381 response.rooms.insert(
382 room_id.to_owned(),
383 assign!(http::response::Room::new(), {
384 unread_notifications: count.clone()
385 }),
386 );
387
388 let sync_response = client
389 .process_sliding_sync(&response, &RequestedRequiredStates::default())
390 .await
391 .expect("Failed to process sync");
392
393 let room = sync_response.rooms.joined.get(room_id).unwrap();
395 assert_eq!(room.unread_notifications, count.clone().into());
396
397 let room = client.get_room(room_id).expect("found room");
399 assert_eq!(room.unread_notification_counts(), count.into());
400 }
401
402 #[async_test]
403 async fn test_can_process_empty_sliding_sync_response() {
404 let client = logged_in_base_client(None).await;
405 let empty_response = http::Response::new("5".to_owned());
406 client
407 .process_sliding_sync(&empty_response, &RequestedRequiredStates::default())
408 .await
409 .expect("Failed to process sync");
410 }
411
412 #[async_test]
413 async fn test_room_with_unspecified_state_is_added_to_client_and_joined_list() {
414 let client = logged_in_base_client(None).await;
416 let room_id = room_id!("!r:e.uk");
417
418 let mut room = http::response::Room::new();
421 room.joined_count = Some(uint!(41));
422 let response = response_with_room(room_id, room);
423 let sync_resp = client
424 .process_sliding_sync(&response, &RequestedRequiredStates::default())
425 .await
426 .expect("Failed to process sync");
427
428 let client_room = client.get_room(room_id).expect("No room found");
430 assert_eq!(client_room.room_id(), room_id);
431 assert_eq!(client_room.joined_members_count(), 41);
432 assert_eq!(client_room.state(), RoomState::Joined);
433
434 assert!(sync_resp.rooms.joined.contains_key(room_id));
436 assert!(!sync_resp.rooms.left.contains_key(room_id));
437 assert!(!sync_resp.rooms.invited.contains_key(room_id));
438 }
439
440 #[async_test]
441 async fn test_missing_room_name_event() {
442 let client = logged_in_base_client(None).await;
444 let room_id = room_id!("!r:e.uk");
445
446 let mut room = http::response::Room::new();
449 room.name = Some("little room".to_owned());
450 let response = response_with_room(room_id, room);
451 let sync_resp = client
452 .process_sliding_sync(&response, &RequestedRequiredStates::default())
453 .await
454 .expect("Failed to process sync");
455
456 let client_room = client.get_room(room_id).expect("No room found");
458 assert!(client_room.name().is_none());
459 assert_eq!(
460 client_room.compute_display_name().await.unwrap().into_inner().to_string(),
461 "Empty Room"
462 );
463 assert_eq!(client_room.state(), RoomState::Joined);
464
465 assert!(sync_resp.rooms.joined.contains_key(room_id));
467 assert!(!sync_resp.rooms.left.contains_key(room_id));
468 assert!(!sync_resp.rooms.invited.contains_key(room_id));
469 assert!(!sync_resp.rooms.knocked.contains_key(room_id));
470 }
471
472 #[async_test]
473 async fn test_room_name_event() {
474 let client = logged_in_base_client(None).await;
476 let room_id = room_id!("!r:e.uk");
477
478 let mut room = http::response::Room::new();
481
482 room.name = Some("little room".to_owned());
483 set_room_name(&mut room, user_id!("@a:b.c"), "The Name".to_owned());
484
485 let response = response_with_room(room_id, room);
486 client
487 .process_sliding_sync(&response, &RequestedRequiredStates::default())
488 .await
489 .expect("Failed to process sync");
490
491 let client_room = client.get_room(room_id).expect("No room found");
493 assert_eq!(client_room.name().as_deref(), Some("The Name"));
494 assert_eq!(
495 client_room.compute_display_name().await.unwrap().into_inner().to_string(),
496 "The Name"
497 );
498 }
499
500 #[async_test]
501 async fn test_missing_invited_room_name_event() {
502 let client = logged_in_base_client(None).await;
504 let room_id = room_id!("!r:e.uk");
505 let user_id = user_id!("@w:e.uk");
506 let inviter = user_id!("@john:mastodon.org");
507
508 let mut room = http::response::Room::new();
511 set_room_invited(&mut room, inviter, user_id);
512 room.name = Some("name from sliding sync response".to_owned());
513 let response = response_with_room(room_id, room);
514 let sync_resp = client
515 .process_sliding_sync(&response, &RequestedRequiredStates::default())
516 .await
517 .expect("Failed to process sync");
518
519 let client_room = client.get_room(room_id).expect("No room found");
521 assert!(client_room.name().is_none());
522
523 assert_eq!(client_room.compute_display_name().await.unwrap().into_inner().to_string(), "w");
525
526 assert_eq!(client_room.state(), RoomState::Invited);
527
528 assert!(!sync_resp.rooms.joined.contains_key(room_id));
530 assert!(!sync_resp.rooms.left.contains_key(room_id));
531 assert!(sync_resp.rooms.invited.contains_key(room_id));
532 assert!(!sync_resp.rooms.knocked.contains_key(room_id));
533 }
534
535 #[async_test]
536 async fn test_invited_room_name_event() {
537 let client = logged_in_base_client(None).await;
539 let room_id = room_id!("!r:e.uk");
540 let user_id = user_id!("@w:e.uk");
541 let inviter = user_id!("@john:mastodon.org");
542
543 let mut room = http::response::Room::new();
546
547 set_room_invited(&mut room, inviter, user_id);
548
549 room.name = Some("name from sliding sync response".to_owned());
550 set_room_name(&mut room, user_id!("@a:b.c"), "The Name".to_owned());
551
552 let response = response_with_room(room_id, room);
553 client
554 .process_sliding_sync(&response, &RequestedRequiredStates::default())
555 .await
556 .expect("Failed to process sync");
557
558 let client_room = client.get_room(room_id).expect("No room found");
560 assert_eq!(client_room.name().as_deref(), Some("The Name"));
561 assert_eq!(
562 client_room.compute_display_name().await.unwrap().into_inner().to_string(),
563 "The Name"
564 );
565 }
566
567 #[async_test]
568 async fn test_receiving_a_knocked_room_membership_event_creates_a_knocked_room() {
569 let client = logged_in_base_client(None).await;
571 let room_id = room_id!("!r:e.uk");
572 let user_id = client.session_meta().unwrap().user_id.to_owned();
573
574 let mut room = http::response::Room::new();
577 set_room_knocked(&mut room, &user_id);
578
579 let response = response_with_room(room_id, room);
580 client
581 .process_sliding_sync(&response, &RequestedRequiredStates::default())
582 .await
583 .expect("Failed to process sync");
584
585 let client_room = client.get_room(room_id).expect("No room found");
587 assert_eq!(client_room.state(), RoomState::Knocked);
588 }
589
590 #[async_test]
591 async fn test_receiving_a_knocked_room_membership_event_with_wrong_state_key_creates_an_invited_room()
592 {
593 let client = logged_in_base_client(None).await;
595 let room_id = room_id!("!r:e.uk");
596 let user_id = user_id!("@w:e.uk");
597
598 let mut room = http::response::Room::new();
600 set_room_knocked(&mut room, user_id);
601
602 let response = response_with_room(room_id, room);
603 client
604 .process_sliding_sync(&response, &RequestedRequiredStates::default())
605 .await
606 .expect("Failed to process sync");
607
608 let client_room = client.get_room(room_id).expect("No room found");
611 assert_eq!(client_room.state(), RoomState::Invited);
612 }
613
614 #[async_test]
615 async fn test_receiving_an_unknown_room_membership_event_in_invite_state_creates_an_invited_room()
616 {
617 let client = logged_in_base_client(None).await;
619 let room_id = room_id!("!r:e.uk");
620 let user_id = client.session_meta().unwrap().user_id.to_owned();
621
622 let mut room = http::response::Room::new();
624 let event = Raw::new(&json!({
625 "type": "m.room.member",
626 "sender": user_id,
627 "content": {
628 "is_direct": true,
629 "membership": "join",
630 },
631 "state_key": user_id,
632 }))
633 .expect("Failed to make raw event")
634 .cast_unchecked();
635 room.invite_state = Some(vec![event]);
636
637 let response = response_with_room(room_id, room);
638 client
639 .process_sliding_sync(&response, &RequestedRequiredStates::default())
640 .await
641 .expect("Failed to process sync");
642
643 let client_room = client.get_room(room_id).expect("No room found");
645 assert_eq!(client_room.state(), RoomState::Invited);
646 }
647
648 #[async_test]
649 async fn test_left_a_room_from_required_state_event() {
650 let client = logged_in_base_client(None).await;
652 let room_id = room_id!("!r:e.uk");
653 let user_id = user_id!("@u:e.uk");
654
655 let mut room = http::response::Room::new();
657 set_room_joined(&mut room, user_id);
658 let response = response_with_room(room_id, room);
659 client
660 .process_sliding_sync(&response, &RequestedRequiredStates::default())
661 .await
662 .expect("Failed to process sync");
663 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Joined);
664
665 let mut room = http::response::Room::new();
667 set_room_left(&mut room, user_id);
668 let response = response_with_room(room_id, room);
669 let sync_resp = client
670 .process_sliding_sync(&response, &RequestedRequiredStates::default())
671 .await
672 .expect("Failed to process sync");
673
674 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Left);
676
677 assert!(!sync_resp.rooms.joined.contains_key(room_id));
679 assert!(sync_resp.rooms.left.contains_key(room_id));
680 assert!(!sync_resp.rooms.invited.contains_key(room_id));
681 assert!(!sync_resp.rooms.knocked.contains_key(room_id));
682 }
683
684 #[async_test]
685 async fn test_kick_or_ban_updates_room_to_left() {
686 for membership in [MembershipState::Leave, MembershipState::Ban] {
687 let room_id = room_id!("!r:e.uk");
688 let user_a_id = user_id!("@a:e.uk");
689 let user_b_id = user_id!("@b:e.uk");
690 let client = logged_in_base_client(Some(user_a_id)).await;
691
692 let mut room = http::response::Room::new();
694 set_room_joined(&mut room, user_a_id);
695 let response = response_with_room(room_id, room);
696 client
697 .process_sliding_sync(&response, &RequestedRequiredStates::default())
698 .await
699 .expect("Failed to process sync");
700 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Joined);
701
702 let mut room = http::response::Room::new();
704 room.required_state.push(make_state_event(
705 user_b_id,
706 user_a_id.as_str(),
707 RoomMemberEventContent::new(membership.clone()),
708 None,
709 ));
710 let response = response_with_room(room_id, room);
711 let sync_resp = client
712 .process_sliding_sync(&response, &RequestedRequiredStates::default())
713 .await
714 .expect("Failed to process sync");
715
716 match membership {
717 MembershipState::Leave => {
718 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Left);
720 }
721 MembershipState::Ban => {
722 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Banned);
724 }
725 _ => panic!("Unexpected membership state found: {membership}"),
726 }
727
728 assert!(!sync_resp.rooms.joined.contains_key(room_id));
730 assert!(sync_resp.rooms.left.contains_key(room_id));
731 assert!(!sync_resp.rooms.invited.contains_key(room_id));
732 assert!(!sync_resp.rooms.knocked.contains_key(room_id));
733 }
734 }
735
736 #[async_test]
737 async fn test_left_a_room_from_timeline_state_event() {
738 let client = logged_in_base_client(None).await;
740 let room_id = room_id!("!r:e.uk");
741 let user_id = user_id!("@u:e.uk");
742
743 let mut room = http::response::Room::new();
745 set_room_joined(&mut room, user_id);
746 let response = response_with_room(room_id, room);
747 client
748 .process_sliding_sync(&response, &RequestedRequiredStates::default())
749 .await
750 .expect("Failed to process sync");
751 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Joined);
752
753 let mut room = http::response::Room::new();
755 set_room_left_as_timeline_event(&mut room, user_id);
756 let response = response_with_room(room_id, room);
757 client
758 .process_sliding_sync(&response, &RequestedRequiredStates::default())
759 .await
760 .expect("Failed to process sync");
761
762 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Joined);
764 }
765
766 #[async_test]
767 async fn test_can_be_reinvited_to_a_left_room() {
768 let client = logged_in_base_client(None).await;
772 let room_id = room_id!("!r:e.uk");
773 let user_id = user_id!("@u:e.uk");
774
775 let mut room = http::response::Room::new();
777 set_room_joined(&mut room, user_id);
778 let response = response_with_room(room_id, room);
779 client
780 .process_sliding_sync(&response, &RequestedRequiredStates::default())
781 .await
782 .expect("Failed to process sync");
783 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Joined);
785
786 let mut room = http::response::Room::new();
788 set_room_left(&mut room, user_id);
789 let response = response_with_room(room_id, room);
790 client
791 .process_sliding_sync(&response, &RequestedRequiredStates::default())
792 .await
793 .expect("Failed to process sync");
794 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Left);
796
797 let mut room = http::response::Room::new();
799 set_room_invited(&mut room, user_id, user_id);
800 let response = response_with_room(room_id, room);
801 client
802 .process_sliding_sync(&response, &RequestedRequiredStates::default())
803 .await
804 .expect("Failed to process sync");
805
806 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Invited);
808 }
809
810 #[async_test]
811 async fn test_other_person_leaving_a_dm_is_reflected_in_their_membership_and_direct_targets() {
812 let room_id = room_id!("!r:e.uk");
813 let user_a_id = user_id!("@a:e.uk");
814 let user_b_id = user_id!("@b:e.uk");
815
816 let client = logged_in_base_client(None).await;
818 create_dm(&client, room_id, user_a_id, user_b_id, MembershipState::Join).await;
819
820 assert!(
822 direct_targets(&client, room_id).contains(<&DirectUserIdentifier>::from(user_b_id))
823 );
824 assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Join);
825
826 update_room_membership(&client, room_id, user_b_id, MembershipState::Leave).await;
828
829 assert!(
833 direct_targets(&client, room_id).contains(<&DirectUserIdentifier>::from(user_b_id))
834 );
835 assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Leave);
836 }
837
838 #[async_test]
839 async fn test_other_person_refusing_invite_to_a_dm_is_reflected_in_their_membership_and_direct_targets()
840 {
841 let room_id = room_id!("!r:e.uk");
842 let user_a_id = user_id!("@a:e.uk");
843 let user_b_id = user_id!("@b:e.uk");
844
845 let client = logged_in_base_client(None).await;
847 create_dm(&client, room_id, user_a_id, user_b_id, MembershipState::Invite).await;
848
849 assert!(
851 direct_targets(&client, room_id).contains(<&DirectUserIdentifier>::from(user_b_id))
852 );
853 assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Invite);
854
855 update_room_membership(&client, room_id, user_b_id, MembershipState::Leave).await;
857
858 assert!(
862 direct_targets(&client, room_id).contains(<&DirectUserIdentifier>::from(user_b_id))
863 );
864 assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Leave);
865 }
866
867 #[async_test]
868 async fn test_members_count_in_a_dm_where_other_person_has_joined() {
869 let room_id = room_id!("!r:bar.org");
870 let user_a_id = user_id!("@a:bar.org");
871 let user_b_id = user_id!("@b:bar.org");
872
873 let client = logged_in_base_client(None).await;
875 create_dm(&client, room_id, user_a_id, user_b_id, MembershipState::Join).await;
876
877 assert_eq!(membership(&client, room_id, user_a_id).await, MembershipState::Join);
879
880 assert!(
882 direct_targets(&client, room_id).contains(<&DirectUserIdentifier>::from(user_b_id))
883 );
884 assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Join);
885
886 let room = client.get_room(room_id).unwrap();
887
888 assert_eq!(room.active_members_count(), 2);
889 assert_eq!(room.joined_members_count(), 2);
890 assert_eq!(room.invited_members_count(), 0);
891 }
892
893 #[async_test]
894 async fn test_members_count_in_a_dm_where_other_person_is_invited() {
895 let room_id = room_id!("!r:bar.org");
896 let user_a_id = user_id!("@a:bar.org");
897 let user_b_id = user_id!("@b:bar.org");
898
899 let client = logged_in_base_client(None).await;
901 create_dm(&client, room_id, user_a_id, user_b_id, MembershipState::Invite).await;
902
903 assert_eq!(membership(&client, room_id, user_a_id).await, MembershipState::Join);
905
906 assert!(
908 direct_targets(&client, room_id).contains(<&DirectUserIdentifier>::from(user_b_id))
909 );
910 assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Invite);
911
912 let room = client.get_room(room_id).unwrap();
913
914 assert_eq!(room.active_members_count(), 2);
915 assert_eq!(room.joined_members_count(), 1);
916 assert_eq!(room.invited_members_count(), 1);
917 }
918
919 #[async_test]
920 async fn test_avatar_is_found_when_processing_sliding_sync_response() {
921 let client = logged_in_base_client(None).await;
923 let room_id = room_id!("!r:e.uk");
924
925 let room = {
927 let mut room = http::response::Room::new();
928 room.avatar = JsOption::from_option(Some(owned_mxc_uri!("mxc://e.uk/med1")));
929
930 room
931 };
932 let response = response_with_room(room_id, room);
933 client
934 .process_sliding_sync(&response, &RequestedRequiredStates::default())
935 .await
936 .expect("Failed to process sync");
937
938 let client_room = client.get_room(room_id).expect("No room found");
940 assert_eq!(
941 client_room.avatar_url().expect("No avatar URL").media_id().expect("No media ID"),
942 "med1"
943 );
944 }
945
946 #[async_test]
947 async fn test_avatar_can_be_unset_when_processing_sliding_sync_response() {
948 let client = logged_in_base_client(None).await;
950 let room_id = room_id!("!r:e.uk");
951
952 let room = {
956 let mut room = http::response::Room::new();
957 room.avatar = JsOption::from_option(Some(owned_mxc_uri!("mxc://e.uk/med1")));
958
959 room
960 };
961 let response = response_with_room(room_id, room);
962 client
963 .process_sliding_sync(&response, &RequestedRequiredStates::default())
964 .await
965 .expect("Failed to process sync");
966
967 let client_room = client.get_room(room_id).expect("No room found");
969 assert_eq!(
970 client_room.avatar_url().expect("No avatar URL").media_id().expect("No media ID"),
971 "med1"
972 );
973
974 let room = http::response::Room::new();
978 let response = response_with_room(room_id, room);
979 client
980 .process_sliding_sync(&response, &RequestedRequiredStates::default())
981 .await
982 .expect("Failed to process sync");
983
984 let client_room = client.get_room(room_id).expect("No room found");
986 assert_eq!(
987 client_room.avatar_url().expect("No avatar URL").media_id().expect("No media ID"),
988 "med1"
989 );
990
991 let room = {
995 let mut room = http::response::Room::new();
996 room.avatar = JsOption::Null;
997
998 room
999 };
1000 let response = response_with_room(room_id, room);
1001 client
1002 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1003 .await
1004 .expect("Failed to process sync");
1005
1006 let client_room = client.get_room(room_id).expect("No room found");
1008 assert!(client_room.avatar_url().is_none());
1009 }
1010
1011 #[async_test]
1012 async fn test_avatar_is_found_from_required_state_when_processing_sliding_sync_response() {
1013 let client = logged_in_base_client(None).await;
1015 let room_id = room_id!("!r:e.uk");
1016 let user_id = user_id!("@u:e.uk");
1017
1018 let room = room_with_avatar(mxc_uri!("mxc://e.uk/med1"), user_id);
1020 let response = response_with_room(room_id, room);
1021 client
1022 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1023 .await
1024 .expect("Failed to process sync");
1025
1026 let client_room = client.get_room(room_id).expect("No room found");
1028 assert_eq!(
1029 client_room.avatar_url().expect("No avatar URL").media_id().expect("No media ID"),
1030 "med1"
1031 );
1032 }
1033
1034 #[async_test]
1035 async fn test_invitation_room_is_added_to_client_and_invite_list() {
1036 let client = logged_in_base_client(None).await;
1038 let room_id = room_id!("!r:e.uk");
1039 let user_id = user_id!("@u:e.uk");
1040
1041 let mut room_info_notable_update = client.room_info_notable_update_receiver();
1042
1043 let mut room = http::response::Room::new();
1045 set_room_invited(&mut room, user_id, user_id);
1046 let response = response_with_room(room_id, room);
1047 let sync_resp = client
1048 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1049 .await
1050 .expect("Failed to process sync");
1051
1052 let client_room = client.get_room(room_id).expect("No room found");
1054 assert_eq!(client_room.room_id(), room_id);
1055 assert_eq!(client_room.state(), RoomState::Invited);
1056
1057 assert!(!sync_resp.rooms.invited[room_id].invite_state.is_empty());
1059 assert!(!sync_resp.rooms.joined.contains_key(room_id));
1060
1061 assert_matches!(
1062 room_info_notable_update.recv().await,
1063 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons }) => {
1064 assert_eq!(received_room_id, room_id);
1065 assert!(reasons.contains(RoomInfoNotableUpdateReasons::MEMBERSHIP));
1067 }
1068 );
1069 assert_matches!(
1070 room_info_notable_update.recv().await,
1071 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons }) => {
1072 assert_eq!(received_room_id, room_id);
1073 assert!(reasons.contains(RoomInfoNotableUpdateReasons::DISPLAY_NAME));
1075 }
1076 );
1077 assert!(room_info_notable_update.is_empty());
1078 }
1079
1080 #[async_test]
1081 async fn test_knock_room_is_added_to_client_and_knock_list() {
1082 let client = logged_in_base_client(None).await;
1084 let room_id = room_id!("!r:e.uk");
1085 let user_id = user_id!("@u:e.uk");
1086
1087 let mut room_info_notable_update = client.room_info_notable_update_receiver();
1088
1089 let mut room = http::response::Room::new();
1091 set_room_knocked(&mut room, user_id);
1092 let response = response_with_room(room_id, room);
1093 let sync_resp = client
1094 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1095 .await
1096 .expect("Failed to process sync");
1097
1098 let client_room = client.get_room(room_id).expect("No room found");
1100 assert_eq!(client_room.room_id(), room_id);
1101 assert_eq!(client_room.state(), RoomState::Knocked);
1102
1103 assert!(!sync_resp.rooms.knocked[room_id].knock_state.is_empty());
1105 assert!(!sync_resp.rooms.joined.contains_key(room_id));
1106
1107 assert_matches!(
1108 room_info_notable_update.recv().await,
1109 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons }) => {
1110 assert_eq!(received_room_id, room_id);
1111 assert!(reasons.contains(RoomInfoNotableUpdateReasons::MEMBERSHIP));
1113 }
1114 );
1115 assert_matches!(
1116 room_info_notable_update.recv().await,
1117 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons }) => {
1118 assert_eq!(received_room_id, room_id);
1119 assert!(reasons.contains(RoomInfoNotableUpdateReasons::DISPLAY_NAME));
1121 }
1122 );
1123 assert!(room_info_notable_update.is_empty());
1124 }
1125
1126 #[async_test]
1127 async fn test_avatar_is_found_in_invitation_room_when_processing_sliding_sync_response() {
1128 let client = logged_in_base_client(None).await;
1130 let room_id = room_id!("!r:e.uk");
1131 let user_id = user_id!("@u:e.uk");
1132
1133 let mut room = room_with_avatar(mxc_uri!("mxc://e.uk/med1"), user_id);
1135 set_room_invited(&mut room, user_id, user_id);
1136 let response = response_with_room(room_id, room);
1137 client
1138 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1139 .await
1140 .expect("Failed to process sync");
1141
1142 let client_room = client.get_room(room_id).expect("No room found");
1144 assert_eq!(
1145 client_room.avatar_url().expect("No avatar URL").media_id().expect("No media ID"),
1146 "med1"
1147 );
1148 }
1149
1150 #[async_test]
1151 async fn test_canonical_alias_is_found_in_invitation_room_when_processing_sliding_sync_response()
1152 {
1153 let client = logged_in_base_client(None).await;
1155 let room_id = room_id!("!r:e.uk");
1156 let user_id = user_id!("@u:e.uk");
1157 let room_alias_id = room_alias_id!("#myroom:e.uk");
1158
1159 let mut room = room_with_canonical_alias(room_alias_id, user_id);
1161 set_room_invited(&mut room, user_id, user_id);
1162 let response = response_with_room(room_id, room);
1163 client
1164 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1165 .await
1166 .expect("Failed to process sync");
1167
1168 let client_room = client.get_room(room_id).expect("No room found");
1170 assert_eq!(client_room.canonical_alias(), Some(room_alias_id.to_owned()));
1171 }
1172
1173 #[async_test]
1174 async fn test_display_name_from_sliding_sync_doesnt_override_alias() {
1175 let client = logged_in_base_client(None).await;
1177 let room_id = room_id!("!r:e.uk");
1178 let user_id = user_id!("@u:e.uk");
1179 let room_alias_id = room_alias_id!("#myroom:e.uk");
1180
1181 let mut room = room_with_canonical_alias(room_alias_id, user_id);
1184 room.name = Some("This came from the server".to_owned());
1185 let response = response_with_room(room_id, room);
1186 client
1187 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1188 .await
1189 .expect("Failed to process sync");
1190
1191 let client_room = client.get_room(room_id).expect("No room found");
1193 assert_eq!(
1194 client_room.compute_display_name().await.unwrap().into_inner().to_string(),
1195 "myroom"
1196 );
1197 assert!(client_room.name().is_none());
1198 }
1199
1200 #[async_test]
1201 async fn test_display_name_is_cached_and_emits_a_notable_update_reason() {
1202 let client = logged_in_base_client(None).await;
1203 let user_id = user_id!("@u:e.uk");
1204 let room_id = room_id!("!r:e.uk");
1205
1206 let mut room_info_notable_update = client.room_info_notable_update_receiver();
1207
1208 let room = room_with_name("Hello World", user_id);
1209 let response = response_with_room(room_id, room);
1210 client
1211 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1212 .await
1213 .expect("Failed to process sync");
1214
1215 let room = client.get_room(room_id).expect("No room found");
1216 assert_eq!(room.cached_display_name().unwrap().to_string(), "Hello World");
1217
1218 assert_matches!(
1219 room_info_notable_update.recv().await,
1220 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons }) => {
1221 assert_eq!(received_room_id, room_id);
1222 assert!(reasons.contains(RoomInfoNotableUpdateReasons::NONE));
1223 }
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::DISPLAY_NAME));
1231 }
1232 );
1233 assert!(room_info_notable_update.is_empty());
1234 }
1235
1236 #[async_test]
1237 async fn test_display_name_is_persisted_from_sliding_sync() {
1238 let user_id = user_id!("@u:e.uk");
1239 let room_id = room_id!("!r:e.uk");
1240 let session_meta = SessionMeta { user_id: user_id.to_owned(), device_id: "FOOBAR".into() };
1241 let state_store;
1242
1243 {
1244 let client = {
1245 let store = StoreConfig::new(CrossProcessLockConfig::SingleProcess);
1246 state_store = store.state_store.clone();
1247
1248 let client = BaseClient::new(store, ThreadingSupport::Disabled);
1249 client
1250 .activate(
1251 session_meta.clone(),
1252 RoomLoadSettings::default(),
1253 #[cfg(feature = "e2e-encryption")]
1254 None,
1255 )
1256 .await
1257 .expect("`activate` failed!");
1258
1259 client
1260 };
1261
1262 let room = room_with_name("Hello World", user_id);
1265 let response = response_with_room(room_id, room);
1266 client
1267 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1268 .await
1269 .expect("Failed to process sync");
1270
1271 let room = client.get_room(room_id).expect("No room found");
1272 assert_eq!(room.cached_display_name().unwrap().to_string(), "Hello World");
1273 }
1274
1275 {
1276 let client = {
1277 let mut store = StoreConfig::new(CrossProcessLockConfig::SingleProcess);
1278 store.state_store = state_store;
1279 let client = BaseClient::new(store, ThreadingSupport::Disabled);
1280 client
1281 .activate(
1282 session_meta,
1283 RoomLoadSettings::default(),
1284 #[cfg(feature = "e2e-encryption")]
1285 None,
1286 )
1287 .await
1288 .expect("`activate` failed!");
1289
1290 client
1291 };
1292
1293 let room = client.get_room(room_id).expect("No room found");
1294 assert_eq!(room.cached_display_name().unwrap().to_string(), "Hello World");
1295 }
1296 }
1297
1298 #[async_test]
1299 async fn test_compute_heroes_from_sliding_sync() {
1300 let client = logged_in_base_client(None).await;
1302 let room_id = room_id!("!r:e.uk");
1303 let gordon = owned_user_id!("@gordon:e.uk");
1304 let alice = owned_user_id!("@alice:e.uk");
1305
1306 let mut room = http::response::Room::new();
1309 room.heroes = Some(vec![
1310 assign!(http::response::Hero::new(gordon), {
1311 name: Some("Gordon".to_owned()),
1312 }),
1313 assign!(http::response::Hero::new(alice), {
1314 name: Some("Alice".to_owned()),
1315 avatar: Some(owned_mxc_uri!("mxc://e.uk/med1"))
1316 }),
1317 ]);
1318 let response = response_with_room(room_id, room);
1319 let _sync_resp = client
1320 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1321 .await
1322 .expect("Failed to process sync");
1323
1324 let client_room = client.get_room(room_id).expect("No room found");
1326 assert_eq!(client_room.room_id(), room_id);
1327 assert_eq!(client_room.state(), RoomState::Joined);
1328
1329 assert_eq!(
1331 client_room.clone_info().summary.heroes(),
1332 &[
1333 RoomHero {
1334 user_id: owned_user_id!("@gordon:e.uk"),
1335 display_name: Some("Gordon".to_owned()),
1336 avatar_url: None
1337 },
1338 RoomHero {
1339 user_id: owned_user_id!("@alice:e.uk"),
1340 display_name: Some("Alice".to_owned()),
1341 avatar_url: Some(owned_mxc_uri!("mxc://e.uk/med1"))
1342 },
1343 ]
1344 );
1345 }
1346
1347 #[async_test]
1348 async fn test_recency_stamp_is_found_when_processing_sliding_sync_response() {
1349 let client = logged_in_base_client(None).await;
1351 let room_id = room_id!("!r:e.uk");
1352
1353 let room = assign!(http::response::Room::new(), {
1355 bump_stamp: Some(42u32.into()),
1356 });
1357 let response = response_with_room(room_id, room);
1358 client
1359 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1360 .await
1361 .expect("Failed to process sync");
1362
1363 let client_room = client.get_room(room_id).expect("No room found");
1365 assert_eq!(client_room.recency_stamp().expect("No recency stamp"), 42.into());
1366 }
1367
1368 #[async_test]
1369 async fn test_recency_stamp_can_be_overwritten_when_present_in_a_sliding_sync_response() {
1370 let client = logged_in_base_client(None).await;
1372 let room_id = room_id!("!r:e.uk");
1373
1374 {
1375 let room = assign!(http::response::Room::new(), {
1377 bump_stamp: Some(42u32.into()),
1378 });
1379 let response = response_with_room(room_id, room);
1380 client
1381 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1382 .await
1383 .expect("Failed to process sync");
1384
1385 let client_room = client.get_room(room_id).expect("No room found");
1387 assert_eq!(client_room.recency_stamp().expect("No recency stamp"), 42.into());
1388 }
1389
1390 {
1391 let room = assign!(http::response::Room::new(), {
1393 bump_stamp: None,
1394 });
1395 let response = response_with_room(room_id, room);
1396 client
1397 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1398 .await
1399 .expect("Failed to process sync");
1400
1401 let client_room = client.get_room(room_id).expect("No room found");
1403 assert_eq!(client_room.recency_stamp().expect("No recency stamp"), 42.into());
1404 }
1405
1406 {
1407 let room = assign!(http::response::Room::new(), {
1410 bump_stamp: Some(153u32.into()),
1411 });
1412 let response = response_with_room(room_id, room);
1413 client
1414 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1415 .await
1416 .expect("Failed to process sync");
1417
1418 let client_room = client.get_room(room_id).expect("No room found");
1420 assert_eq!(client_room.recency_stamp().expect("No recency stamp"), 153.into());
1421 }
1422 }
1423
1424 #[async_test]
1425 async fn test_recency_stamp_can_trigger_a_notable_update_reason() {
1426 let client = logged_in_base_client(None).await;
1428 let mut room_info_notable_update_stream = client.room_info_notable_update_receiver();
1429 let room_id = room_id!("!r:e.uk");
1430
1431 let room = assign!(http::response::Room::new(), {
1433 bump_stamp: Some(42u32.into()),
1434 });
1435 let response = response_with_room(room_id, room);
1436 client
1437 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1438 .await
1439 .expect("Failed to process sync");
1440
1441 assert_matches!(
1444 room_info_notable_update_stream.recv().await,
1445 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1446 assert_eq!(received_room_id, room_id);
1447 assert!(!received_reasons.contains(RoomInfoNotableUpdateReasons::RECENCY_STAMP));
1448 }
1449 );
1450 assert_matches!(
1451 room_info_notable_update_stream.recv().await,
1452 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1453 assert_eq!(received_room_id, room_id);
1454 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::DISPLAY_NAME));
1455 }
1456 );
1457 assert!(room_info_notable_update_stream.is_empty());
1458
1459 let room = assign!(http::response::Room::new(), {
1461 bump_stamp: Some(43u32.into()),
1462 });
1463 let response = response_with_room(room_id, room);
1464 client
1465 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1466 .await
1467 .expect("Failed to process sync");
1468
1469 assert_matches!(
1471 room_info_notable_update_stream.recv().await,
1472 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1473 assert_eq!(received_room_id, room_id);
1474 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::RECENCY_STAMP));
1475 }
1476 );
1477 assert!(room_info_notable_update_stream.is_empty());
1478 }
1479
1480 #[async_test]
1481 async fn test_leaving_room_can_trigger_a_notable_update_reason() {
1482 let client = logged_in_base_client(None).await;
1484 let mut room_info_notable_update_stream = client.room_info_notable_update_receiver();
1485
1486 let room_id = room_id!("!r:e.uk");
1488 let room = http::response::Room::new();
1489 let response = response_with_room(room_id, room);
1490 client
1491 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1492 .await
1493 .expect("Failed to process sync");
1494
1495 assert_matches!(
1497 room_info_notable_update_stream.recv().await,
1498 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1499 assert_eq!(received_room_id, room_id);
1500 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::NONE));
1501 }
1502 );
1503 assert_matches!(
1504 room_info_notable_update_stream.recv().await,
1505 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1506 assert_eq!(received_room_id, room_id);
1507 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::DISPLAY_NAME));
1508 }
1509 );
1510
1511 let room_id = room_id!("!r:e.uk");
1513 let events = vec![
1514 Raw::from_json_string(
1515 json!({
1516 "type": "m.room.member",
1517 "event_id": "$3",
1518 "content": { "membership": "join" },
1519 "sender": "@u:h.uk",
1520 "origin_server_ts": 12344445,
1521 "state_key": "@u:e.uk",
1522 })
1523 .to_string(),
1524 )
1525 .unwrap(),
1526 ];
1527 let room = assign!(http::response::Room::new(), {
1528 required_state: events,
1529 });
1530 let response = response_with_room(room_id, room);
1531 client
1532 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1533 .await
1534 .expect("Failed to process sync");
1535
1536 assert_matches!(
1538 room_info_notable_update_stream.recv().await,
1539 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1540 assert_eq!(received_room_id, room_id);
1541 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::NONE));
1542 }
1543 );
1544 assert!(room_info_notable_update_stream.is_empty());
1545
1546 let events = vec![
1547 Raw::from_json_string(
1548 json!({
1549 "type": "m.room.member",
1550 "event_id": "$3",
1551 "content": { "membership": "leave" },
1552 "sender": "@u:h.uk",
1553 "origin_server_ts": 12344445,
1554 "state_key": "@u:e.uk",
1555 })
1556 .to_string(),
1557 )
1558 .unwrap(),
1559 ];
1560 let room = assign!(http::response::Room::new(), {
1561 required_state: events,
1562 });
1563 let response = response_with_room(room_id, room);
1564 client
1565 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1566 .await
1567 .expect("Failed to process sync");
1568
1569 assert_matches!(
1571 room_info_notable_update_stream.recv().await,
1572 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1573 assert_eq!(received_room_id, room_id);
1574 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::MEMBERSHIP));
1575 }
1576 );
1577 assert!(room_info_notable_update_stream.is_empty());
1578 }
1579
1580 #[async_test]
1581 async fn test_unread_marker_can_trigger_a_notable_update_reason() {
1582 let client = logged_in_base_client(None).await;
1584 let mut room_info_notable_update_stream = client.room_info_notable_update_receiver();
1585
1586 let room_id = room_id!("!r:e.uk");
1588 let room = http::response::Room::new();
1589 let response = response_with_room(room_id, room);
1590 client
1591 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1592 .await
1593 .expect("Failed to process sync");
1594
1595 assert_matches!(
1597 room_info_notable_update_stream.recv().await,
1598 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1599 assert_eq!(received_room_id, room_id);
1600 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::NONE), "{received_reasons:?}");
1601 }
1602 );
1603 assert_matches!(
1604 room_info_notable_update_stream.recv().await,
1605 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1606 assert_eq!(received_room_id, room_id);
1607 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::DISPLAY_NAME), "{received_reasons:?}");
1608 }
1609 );
1610 assert!(room_info_notable_update_stream.is_empty());
1611
1612 let room_id = room_id!("!r:e.uk");
1615 let room_account_data_events = vec![
1616 Raw::from_json_string(
1617 json!({
1618 "type": "m.marked_unread",
1619 "event_id": "$1",
1620 "content": { "unread": true },
1621 "sender": client.session_meta().unwrap().user_id,
1622 "origin_server_ts": 12344445,
1623 })
1624 .to_string(),
1625 )
1626 .unwrap(),
1627 ];
1628 let mut response = response_with_room(room_id, http::response::Room::new());
1629 response.extensions.account_data.rooms.insert(room_id.to_owned(), room_account_data_events);
1630
1631 client
1632 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1633 .await
1634 .expect("Failed to process sync");
1635
1636 assert_matches!(
1638 room_info_notable_update_stream.recv().await,
1639 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1640 assert_eq!(received_room_id, room_id);
1641 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::UNREAD_MARKER), "{received_reasons:?}");
1642 }
1643 );
1644
1645 client
1647 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1648 .await
1649 .expect("Failed to process sync");
1650
1651 assert_matches!(
1652 room_info_notable_update_stream.recv().await,
1653 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1654 assert_eq!(received_room_id, room_id);
1655 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::NONE), "{received_reasons:?}");
1656 }
1657 );
1658 assert!(room_info_notable_update_stream.is_empty());
1659
1660 let room_account_data_events = vec![
1662 Raw::from_json_string(
1663 json!({
1664 "type": "m.marked_unread",
1665 "event_id": "$1",
1666 "content": { "unread": false },
1667 "sender": client.session_meta().unwrap().user_id,
1668 "origin_server_ts": 12344445,
1669 })
1670 .to_string(),
1671 )
1672 .unwrap(),
1673 ];
1674 response.extensions.account_data.rooms.insert(room_id.to_owned(), room_account_data_events);
1675 client
1676 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1677 .await
1678 .expect("Failed to process sync");
1679
1680 assert_matches!(
1681 room_info_notable_update_stream.recv().await,
1682 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1683 assert_eq!(received_room_id, room_id);
1684 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::UNREAD_MARKER));
1685 }
1686 );
1687 assert!(room_info_notable_update_stream.is_empty());
1688 }
1689
1690 #[async_test]
1691 async fn test_unstable_unread_marker_is_ignored_after_stable() {
1692 let client = logged_in_base_client(None).await;
1694 let mut room_info_notable_update_stream = client.room_info_notable_update_receiver();
1695
1696 let room_id = room_id!("!r:e.uk");
1698 let room = http::response::Room::new();
1699 let response = response_with_room(room_id, room);
1700 client
1701 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1702 .await
1703 .expect("Failed to process sync");
1704
1705 assert_matches!(
1707 room_info_notable_update_stream.recv().await,
1708 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1709 assert_eq!(received_room_id, room_id);
1710 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::NONE), "{received_reasons:?}");
1711 }
1712 );
1713 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::DISPLAY_NAME), "{received_reasons:?}");
1718 }
1719 );
1720 assert!(room_info_notable_update_stream.is_empty());
1721
1722 let room_id = room_id!("!r:e.uk");
1725 let unstable_room_account_data_events = vec![
1726 Raw::from_json_string(
1727 json!({
1728 "type": "com.famedly.marked_unread",
1729 "event_id": "$1",
1730 "content": { "unread": true },
1731 "sender": client.session_meta().unwrap().user_id,
1732 "origin_server_ts": 12344445,
1733 })
1734 .to_string(),
1735 )
1736 .unwrap(),
1737 ];
1738 let mut response = response_with_room(room_id, http::response::Room::new());
1739 response
1740 .extensions
1741 .account_data
1742 .rooms
1743 .insert(room_id.to_owned(), unstable_room_account_data_events.clone());
1744
1745 client
1746 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1747 .await
1748 .expect("Failed to process sync");
1749
1750 assert_matches!(
1752 room_info_notable_update_stream.recv().await,
1753 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1754 assert_eq!(received_room_id, room_id);
1755 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::UNREAD_MARKER), "{received_reasons:?}");
1756 }
1757 );
1758 assert!(room_info_notable_update_stream.is_empty());
1759
1760 let stable_room_account_data_events = vec![
1762 Raw::from_json_string(
1763 json!({
1764 "type": "m.marked_unread",
1765 "event_id": "$1",
1766 "content": { "unread": false },
1767 "sender": client.session_meta().unwrap().user_id,
1768 "origin_server_ts": 12344445,
1769 })
1770 .to_string(),
1771 )
1772 .unwrap(),
1773 ];
1774 response
1775 .extensions
1776 .account_data
1777 .rooms
1778 .insert(room_id.to_owned(), stable_room_account_data_events);
1779 client
1780 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1781 .await
1782 .expect("Failed to process sync");
1783
1784 assert_matches!(
1786 room_info_notable_update_stream.recv().await,
1787 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1788 assert_eq!(received_room_id, room_id);
1789 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::UNREAD_MARKER));
1790 }
1791 );
1792 assert!(room_info_notable_update_stream.is_empty());
1793
1794 response
1797 .extensions
1798 .account_data
1799 .rooms
1800 .insert(room_id.to_owned(), unstable_room_account_data_events);
1801 client
1802 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1803 .await
1804 .expect("Failed to process sync");
1805
1806 assert_matches!(
1808 room_info_notable_update_stream.recv().await,
1809 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1810 assert_eq!(received_room_id, room_id);
1811 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::NONE), "{received_reasons:?}");
1812 }
1813 );
1814 assert!(room_info_notable_update_stream.is_empty());
1815
1816 let stable_room_account_data_events = vec![
1819 Raw::from_json_string(
1820 json!({
1821 "type": "m.marked_unread",
1822 "event_id": "$3",
1823 "content": { "unread": true },
1824 "sender": client.session_meta().unwrap().user_id,
1825 "origin_server_ts": 12344445,
1826 })
1827 .to_string(),
1828 )
1829 .unwrap(),
1830 ];
1831 response
1832 .extensions
1833 .account_data
1834 .rooms
1835 .insert(room_id.to_owned(), stable_room_account_data_events);
1836 client
1837 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1838 .await
1839 .expect("Failed to process sync");
1840
1841 assert_matches!(
1843 room_info_notable_update_stream.recv().await,
1844 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1845 assert_eq!(received_room_id, room_id);
1846 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::UNREAD_MARKER));
1847 }
1848 );
1849 assert!(room_info_notable_update_stream.is_empty());
1850 }
1851
1852 #[async_test]
1853 async fn test_pinned_events_are_updated_on_sync() {
1854 let user_a_id = user_id!("@a:e.uk");
1855 let client = logged_in_base_client(Some(user_a_id)).await;
1856 let room_id = room_id!("!r:e.uk");
1857 let pinned_event_id = owned_event_id!("$an-id:e.uk");
1858
1859 let mut room_response = http::response::Room::new();
1861 set_room_joined(&mut room_response, user_a_id);
1862 let response = response_with_room(room_id, room_response);
1863 client
1864 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1865 .await
1866 .expect("Failed to process sync");
1867
1868 let room = client.get_room(room_id).unwrap();
1870 let pinned_event_ids = room.pinned_event_ids();
1871 assert_matches!(pinned_event_ids, None);
1872
1873 let mut room_response = http::response::Room::new();
1875 room_response.required_state.push(make_state_event(
1876 user_a_id,
1877 "",
1878 RoomPinnedEventsEventContent::new(vec![pinned_event_id.clone()]),
1879 None,
1880 ));
1881 let response = response_with_room(room_id, room_response);
1882 client
1883 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1884 .await
1885 .expect("Failed to process sync");
1886
1887 let pinned_event_ids = room.pinned_event_ids().unwrap_or_default();
1888 assert_eq!(pinned_event_ids.len(), 1);
1889 assert_eq!(pinned_event_ids[0], pinned_event_id);
1890
1891 let mut room_response = http::response::Room::new();
1893 room_response.required_state.push(make_state_event(
1894 user_a_id,
1895 "",
1896 RoomPinnedEventsEventContent::new(Vec::new()),
1897 None,
1898 ));
1899 let response = response_with_room(room_id, room_response);
1900 client
1901 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1902 .await
1903 .expect("Failed to process sync");
1904 let pinned_event_ids = room.pinned_event_ids().unwrap();
1905 assert!(pinned_event_ids.is_empty());
1906 }
1907
1908 #[async_test]
1909 async fn test_dms_are_processed_in_any_sync_response() {
1910 let current_user_id = user_id!("@current:e.uk");
1911 let client = logged_in_base_client(Some(current_user_id)).await;
1912 let user_a_id = user_id!("@a:e.uk");
1913 let user_b_id = user_id!("@b:e.uk");
1914 let room_id_1 = room_id!("!r:e.uk");
1915 let room_id_2 = room_id!("!s:e.uk");
1916
1917 let mut room_response = http::response::Room::new();
1918 set_room_joined(&mut room_response, user_a_id);
1919 let mut response = response_with_room(room_id_1, room_response);
1920 let mut direct_content: BTreeMap<OwnedDirectUserIdentifier, Vec<OwnedRoomId>> =
1921 BTreeMap::new();
1922 direct_content.insert(user_a_id.into(), vec![room_id_1.to_owned()]);
1923 direct_content.insert(user_b_id.into(), vec![room_id_2.to_owned()]);
1924 response
1925 .extensions
1926 .account_data
1927 .global
1928 .push(make_global_account_data_event(DirectEventContent(direct_content)));
1929 client
1930 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1931 .await
1932 .expect("Failed to process sync");
1933
1934 let room_1 = client.get_room(room_id_1).unwrap();
1935 assert!(room_1.is_direct().await.unwrap());
1936
1937 let mut room_response = http::response::Room::new();
1939 set_room_joined(&mut room_response, user_b_id);
1940 let response = response_with_room(room_id_2, room_response);
1941 client
1942 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1943 .await
1944 .expect("Failed to process sync");
1945
1946 let room_2 = client.get_room(room_id_2).unwrap();
1947 assert!(room_2.is_direct().await.unwrap());
1948 }
1949
1950 #[async_test]
1951 async fn test_room_encryption_state_is_and_is_not_encrypted() {
1952 let user_id = user_id!("@raclette:patate");
1953 let client = logged_in_base_client(Some(user_id)).await;
1954 let room_id_0 = room_id!("!r0");
1955 let room_id_1 = room_id!("!r1");
1956 let room_id_2 = room_id!("!r2");
1957
1958 let requested_required_states = RequestedRequiredStates::from(&{
1975 let mut request = http::Request::new();
1976
1977 request.room_subscriptions.insert(room_id_0.to_owned(), {
1978 let mut room_subscription = http::request::RoomSubscription::default();
1979
1980 room_subscription
1981 .required_state
1982 .push((StateEventType::RoomEncryption, "".to_owned()));
1983
1984 room_subscription
1985 });
1986
1987 request
1988 });
1989
1990 let mut response = http::Response::new("0".to_owned());
1991
1992 {
1996 let not_encrypted_room = http::response::Room::new();
1997 let mut encrypted_room = http::response::Room::new();
1998 set_room_is_encrypted(&mut encrypted_room, user_id);
1999
2000 response.rooms.insert(room_id_0.to_owned(), encrypted_room.clone());
2001 response.rooms.insert(room_id_1.to_owned(), encrypted_room);
2002 response.rooms.insert(room_id_2.to_owned(), not_encrypted_room);
2003 }
2004
2005 client
2006 .process_sliding_sync(&response, &requested_required_states)
2007 .await
2008 .expect("Failed to process sync");
2009
2010 assert_matches!(
2012 client.get_room(room_id_0).unwrap().encryption_state(),
2013 EncryptionState::Encrypted
2014 );
2015 assert_matches!(
2016 client.get_room(room_id_1).unwrap().encryption_state(),
2017 EncryptionState::Encrypted
2018 );
2019 assert_matches!(
2021 client.get_room(room_id_2).unwrap().encryption_state(),
2022 EncryptionState::NotEncrypted
2023 )
2024 }
2025
2026 #[async_test]
2027 async fn test_room_encryption_state_is_unknown() {
2028 let user_id = user_id!("@raclette:patate");
2029 let client = logged_in_base_client(Some(user_id)).await;
2030 let room_id_0 = room_id!("!r0");
2031 let room_id_1 = room_id!("!r1");
2032
2033 let requested_required_states = RequestedRequiredStates::from(&http::Request::new());
2046
2047 let mut response = http::Response::new("0".to_owned());
2048
2049 {
2051 let not_encrypted_room = http::response::Room::new();
2052 let mut encrypted_room = http::response::Room::new();
2053 set_room_is_encrypted(&mut encrypted_room, user_id);
2054
2055 response.rooms.insert(room_id_0.to_owned(), encrypted_room);
2056 response.rooms.insert(room_id_1.to_owned(), not_encrypted_room);
2057 }
2058
2059 client
2060 .process_sliding_sync(&response, &requested_required_states)
2061 .await
2062 .expect("Failed to process sync");
2063
2064 assert_matches!(
2067 client.get_room(room_id_0).unwrap().encryption_state(),
2068 EncryptionState::Encrypted
2069 );
2070 assert_matches!(
2073 client.get_room(room_id_1).unwrap().encryption_state(),
2074 EncryptionState::Unknown
2075 );
2076 }
2077
2078 async fn membership(
2079 client: &BaseClient,
2080 room_id: &RoomId,
2081 user_id: &UserId,
2082 ) -> MembershipState {
2083 let room = client.get_room(room_id).expect("Room not found!");
2084 let member = room.get_member(user_id).await.unwrap().expect("B not in room");
2085 member.membership().clone()
2086 }
2087
2088 fn direct_targets(client: &BaseClient, room_id: &RoomId) -> HashSet<OwnedDirectUserIdentifier> {
2089 let room = client.get_room(room_id).expect("Room not found!");
2090 room.direct_targets()
2091 }
2092
2093 async fn create_dm(
2096 client: &BaseClient,
2097 room_id: &RoomId,
2098 my_id: &UserId,
2099 their_id: &UserId,
2100 other_state: MembershipState,
2101 ) {
2102 let mut room = http::response::Room::new();
2103 set_room_joined(&mut room, my_id);
2104
2105 match other_state {
2106 MembershipState::Join => {
2107 room.joined_count = Some(uint!(2));
2108 room.invited_count = None;
2109 }
2110
2111 MembershipState::Invite => {
2112 room.joined_count = Some(uint!(1));
2113 room.invited_count = Some(uint!(1));
2114 }
2115
2116 _ => {
2117 room.joined_count = Some(uint!(1));
2118 room.invited_count = None;
2119 }
2120 }
2121
2122 room.required_state.push(make_membership_event(their_id, other_state));
2123
2124 let mut response = response_with_room(room_id, room);
2125 set_direct_with(&mut response, their_id.to_owned(), vec![room_id.to_owned()]);
2126 client
2127 .process_sliding_sync(&response, &RequestedRequiredStates::default())
2128 .await
2129 .expect("Failed to process sync");
2130 }
2131
2132 async fn update_room_membership(
2134 client: &BaseClient,
2135 room_id: &RoomId,
2136 user_id: &UserId,
2137 new_state: MembershipState,
2138 ) {
2139 let mut room = http::response::Room::new();
2140 room.required_state.push(make_membership_event(user_id, new_state));
2141 let response = response_with_room(room_id, room);
2142 client
2143 .process_sliding_sync(&response, &RequestedRequiredStates::default())
2144 .await
2145 .expect("Failed to process sync");
2146 }
2147
2148 fn set_direct_with(
2149 response: &mut http::Response,
2150 user_id: OwnedUserId,
2151 room_ids: Vec<OwnedRoomId>,
2152 ) {
2153 let mut direct_content: BTreeMap<OwnedDirectUserIdentifier, Vec<OwnedRoomId>> =
2154 BTreeMap::new();
2155 direct_content.insert(user_id.into(), room_ids);
2156 response
2157 .extensions
2158 .account_data
2159 .global
2160 .push(make_global_account_data_event(DirectEventContent(direct_content)));
2161 }
2162
2163 fn response_with_room(room_id: &RoomId, room: http::response::Room) -> http::Response {
2164 let mut response = http::Response::new("5".to_owned());
2165 response.rooms.insert(room_id.to_owned(), room);
2166 response
2167 }
2168
2169 fn room_with_avatar(avatar_uri: &MxcUri, user_id: &UserId) -> http::response::Room {
2170 let mut room = http::response::Room::new();
2171
2172 let mut avatar_event_content = RoomAvatarEventContent::new();
2173 avatar_event_content.url = Some(avatar_uri.to_owned());
2174
2175 room.required_state.push(make_state_event(user_id, "", avatar_event_content, None));
2176
2177 room
2178 }
2179
2180 fn room_with_canonical_alias(
2181 room_alias_id: &RoomAliasId,
2182 user_id: &UserId,
2183 ) -> http::response::Room {
2184 let mut room = http::response::Room::new();
2185
2186 let mut canonical_alias_event_content = RoomCanonicalAliasEventContent::new();
2187 canonical_alias_event_content.alias = Some(room_alias_id.to_owned());
2188
2189 room.required_state.push(make_state_event(
2190 user_id,
2191 "",
2192 canonical_alias_event_content,
2193 None,
2194 ));
2195
2196 room
2197 }
2198
2199 fn room_with_name(name: &str, user_id: &UserId) -> http::response::Room {
2200 let mut room = http::response::Room::new();
2201
2202 let name_event_content = RoomNameEventContent::new(name.to_owned());
2203
2204 room.required_state.push(make_state_event(user_id, "", name_event_content, None));
2205
2206 room
2207 }
2208
2209 fn set_room_name(room: &mut http::response::Room, sender: &UserId, name: String) {
2210 room.required_state.push(make_state_event(
2211 sender,
2212 "",
2213 RoomNameEventContent::new(name),
2214 None,
2215 ));
2216 }
2217
2218 fn set_room_invited(room: &mut http::response::Room, inviter: &UserId, invitee: &UserId) {
2219 let evt = Raw::new(&json!({
2223 "type": "m.room.member",
2224 "sender": inviter,
2225 "content": {
2226 "is_direct": true,
2227 "membership": "invite",
2228 },
2229 "state_key": invitee,
2230 }))
2231 .expect("Failed to make raw event")
2232 .cast_unchecked();
2233
2234 room.invite_state = Some(vec![evt]);
2235
2236 room.required_state.push(make_state_event(
2239 inviter,
2240 invitee.as_str(),
2241 RoomMemberEventContent::new(MembershipState::Invite),
2242 None,
2243 ));
2244 }
2245
2246 fn set_room_knocked(room: &mut http::response::Room, knocker: &UserId) {
2247 let evt = Raw::new(&json!({
2251 "type": "m.room.member",
2252 "sender": knocker,
2253 "content": {
2254 "is_direct": true,
2255 "membership": "knock",
2256 },
2257 "state_key": knocker,
2258 }))
2259 .expect("Failed to make raw event")
2260 .cast_unchecked();
2261
2262 room.invite_state = Some(vec![evt]);
2263 }
2264
2265 fn set_room_joined(room: &mut http::response::Room, user_id: &UserId) {
2266 room.required_state.push(make_membership_event(user_id, MembershipState::Join));
2267 }
2268
2269 fn set_room_left(room: &mut http::response::Room, user_id: &UserId) {
2270 room.required_state.push(make_membership_event(user_id, MembershipState::Leave));
2271 }
2272
2273 fn set_room_left_as_timeline_event(room: &mut http::response::Room, user_id: &UserId) {
2274 room.timeline.push(make_membership_event(user_id, MembershipState::Leave));
2275 }
2276
2277 fn set_room_is_encrypted(room: &mut http::response::Room, user_id: &UserId) {
2278 room.required_state.push(make_encryption_event(user_id));
2279 }
2280
2281 fn make_membership_event<K>(user_id: &UserId, state: MembershipState) -> Raw<K> {
2282 make_state_event(user_id, user_id.as_str(), RoomMemberEventContent::new(state), None)
2283 }
2284
2285 fn make_encryption_event<K>(user_id: &UserId) -> Raw<K> {
2286 make_state_event(user_id, "", RoomEncryptionEventContent::with_recommended_defaults(), None)
2287 }
2288
2289 fn make_global_account_data_event<C: GlobalAccountDataEventContent, E>(content: C) -> Raw<E> {
2290 Raw::new(&json!({
2291 "type": content.event_type(),
2292 "content": content,
2293 }))
2294 .expect("Failed to create account data event")
2295 .cast_unchecked()
2296 }
2297
2298 fn make_state_event<C: StateEventContent, E>(
2299 sender: &UserId,
2300 state_key: &str,
2301 content: C,
2302 prev_content: Option<C>,
2303 ) -> Raw<E> {
2304 let unsigned = if let Some(prev_content) = prev_content {
2305 json!({ "prev_content": prev_content })
2306 } else {
2307 json!({})
2308 };
2309
2310 Raw::new(&json!({
2311 "type": content.event_type(),
2312 "state_key": state_key,
2313 "content": content,
2314 "event_id": event_id!("$evt"),
2315 "sender": sender,
2316 "origin_server_ts": 10,
2317 "unsigned": unsigned,
2318 }))
2319 .expect("Failed to create state event")
2320 .cast_unchecked()
2321 }
2322}