1#[cfg(feature = "e2e-encryption")]
18use matrix_sdk_common::deserialized_responses::ProcessedToDeviceEvent;
19use matrix_sdk_common::{deserialized_responses::TimelineEvent, 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 read_receipts::compute_unread_counts,
31 response_processors as processors,
32 room::RoomInfoNotableUpdateReasons,
33 store::ambiguity_map::AmbiguityCache,
34 sync::{RoomUpdates, SyncResponse},
35};
36
37impl BaseClient {
38 #[cfg(feature = "e2e-encryption")]
46 pub async fn process_sliding_sync_e2ee(
47 &self,
48 to_device: Option<&http::response::ToDevice>,
49 e2ee: &http::response::E2EE,
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 &self.ignore_user_list_changes,
81 None,
82 )
83 .await?;
84
85 Ok(Some(processed_to_device_events))
86 }
87
88 #[instrument(skip_all, level = "trace")]
95 pub async fn process_sliding_sync(
96 &self,
97 response: &http::Response,
98 requested_required_states: &RequestedRequiredStates,
99 ) -> Result<SyncResponse> {
100 let http::Response { rooms, lists, extensions, .. } = response;
101
102 trace!(
103 rooms = rooms.len(),
104 lists = lists.len(),
105 has_extensions = !extensions.is_empty(),
106 "Processing sliding sync room events"
107 );
108
109 if rooms.is_empty() && extensions.is_empty() {
110 return Ok(SyncResponse::default());
113 }
114
115 let _timer = timer!(tracing::Level::TRACE, "_method");
116
117 let mut context = processors::Context::default();
118
119 let state_store = self.state_store.clone();
120 let mut ambiguity_cache = AmbiguityCache::new(state_store.inner.clone());
121
122 let global_account_data_processor =
123 processors::account_data::global(&extensions.account_data.global);
124 let push_rules = self.get_push_rules(&global_account_data_processor).await?;
125
126 let mut room_updates = RoomUpdates::default();
127 let mut notifications = Default::default();
128
129 let user_id = self
130 .session_meta()
131 .expect("Sliding sync shouldn't run without an authenticated user")
132 .user_id
133 .to_owned();
134
135 for (room_id, room_response) in rooms {
136 let Some((room_info, room_update)) = processors::room::msc4186::update_any_room(
137 &mut context,
138 &user_id,
139 processors::room::RoomCreationData::new(
140 room_id,
141 requested_required_states,
142 &mut ambiguity_cache,
143 ),
144 room_response,
145 &extensions.account_data.rooms,
146 #[cfg(feature = "e2e-encryption")]
147 processors::e2ee::E2EE::new(
148 self.olm_machine().await.as_ref(),
149 &self.decryption_settings,
150 self.handle_verification_events,
151 ),
152 processors::notification::Notification::new(
153 &push_rules,
154 &mut notifications,
155 &self.state_store,
156 ),
157 )
158 .await?
159 else {
160 continue;
161 };
162
163 context.state_changes.add_room(room_info);
164
165 let room_id = room_id.to_owned();
166
167 use processors::room::msc4186::RoomUpdateKind;
168
169 match room_update {
170 RoomUpdateKind::Joined(joined_room_update) => {
171 room_updates.joined.insert(room_id, joined_room_update);
172 }
173 RoomUpdateKind::Left(left_room_update) => {
174 room_updates.left.insert(room_id, left_room_update);
175 }
176 RoomUpdateKind::Invited(invited_room_update) => {
177 room_updates.invited.insert(room_id, invited_room_update);
178 }
179 RoomUpdateKind::Knocked(knocked_room_update) => {
180 room_updates.knocked.insert(room_id, knocked_room_update);
181 }
182 }
183 }
184
185 processors::room::msc4186::extensions::dispatch_typing_ephemeral_events(
189 &extensions.typing,
190 &mut room_updates.joined,
191 );
192
193 processors::room::msc4186::extensions::room_account_data(
195 &mut context,
196 &extensions.account_data,
197 &mut room_updates,
198 &self.state_store,
199 );
200
201 global_account_data_processor.apply(&mut context, &state_store).await;
202
203 context.state_changes.ambiguity_maps = ambiguity_cache.cache;
204
205 processors::changes::save_and_apply(
207 context,
208 &self.state_store,
209 &self.ignore_user_list_changes,
210 None,
211 )
212 .await?;
213
214 let mut context = processors::Context::default();
215
216 processors::room::display_name::update_for_rooms(
219 &mut context,
220 &room_updates,
221 &self.state_store,
222 )
223 .await;
224
225 processors::changes::save_only(context, &self.state_store).await?;
227
228 Ok(SyncResponse {
229 rooms: room_updates,
230 notifications,
231 presence: Default::default(),
232 account_data: extensions.account_data.global.clone(),
233 to_device: Default::default(),
234 })
235 }
236
237 #[doc(hidden)]
240 pub async fn process_sliding_sync_receipts_extension_for_room(
241 &self,
242 room_id: &OwnedRoomId,
243 response: &http::Response,
244 new_sync_events: Vec<TimelineEvent>,
245 room_previous_events: Vec<TimelineEvent>,
246 ) -> Result<Option<Raw<SyncReceiptEvent>>> {
247 let mut context = processors::Context::default();
248
249 let mut save_context = false;
250
251 let receipt_ephemeral_event = if let Some(receipt_ephemeral_event) =
253 response.extensions.receipts.rooms.get(room_id)
254 {
255 processors::room::msc4186::extensions::dispatch_receipt_ephemeral_event_for_room(
256 &mut context,
257 room_id,
258 receipt_ephemeral_event,
259 );
260 save_context = true;
261 Some(receipt_ephemeral_event.clone())
262 } else {
263 None
264 };
265
266 let user_id = &self.session_meta().expect("logged in user").user_id;
267
268 if let Some(mut room_info) = self.get_room(room_id).map(|room| room.clone_info()) {
271 let prev_read_receipts = room_info.read_receipts.clone();
272
273 compute_unread_counts(
274 user_id,
275 room_id,
276 context.state_changes.receipts.get(room_id),
277 room_previous_events,
278 &new_sync_events,
279 &mut room_info.read_receipts,
280 self.threading_support,
281 );
282
283 if prev_read_receipts != room_info.read_receipts {
284 context
285 .room_info_notable_updates
286 .entry(room_id.clone())
287 .or_default()
288 .insert(RoomInfoNotableUpdateReasons::READ_RECEIPT);
289
290 context.state_changes.add_room(room_info);
291 save_context = true;
292 }
293 }
294
295 if save_context {
297 processors::changes::save_only(context, &self.state_store).await?;
298 }
299
300 Ok(receipt_ephemeral_event)
301 }
302}
303
304#[cfg(all(test, not(target_family = "wasm")))]
305mod tests {
306 use std::collections::{BTreeMap, HashSet};
307
308 use assert_matches::assert_matches;
309 use matrix_sdk_test::async_test;
310 use ruma::{
311 JsOption, MxcUri, OwnedRoomId, OwnedUserId, RoomAliasId, RoomId, UserId,
312 api::client::sync::sync_events::UnreadNotificationsCount,
313 assign, event_id,
314 events::{
315 GlobalAccountDataEventContent, StateEventContent, StateEventType,
316 direct::{DirectEventContent, DirectUserIdentifier, OwnedDirectUserIdentifier},
317 room::{
318 avatar::RoomAvatarEventContent,
319 canonical_alias::RoomCanonicalAliasEventContent,
320 encryption::RoomEncryptionEventContent,
321 member::{MembershipState, RoomMemberEventContent},
322 name::RoomNameEventContent,
323 pinned_events::RoomPinnedEventsEventContent,
324 },
325 },
326 mxc_uri, owned_event_id, owned_mxc_uri, owned_user_id, room_alias_id, room_id,
327 serde::Raw,
328 uint, user_id,
329 };
330 use serde_json::json;
331
332 use super::http;
333 use crate::{
334 BaseClient, EncryptionState, RequestedRequiredStates, RoomInfoNotableUpdate, RoomState,
335 SessionMeta,
336 client::ThreadingSupport,
337 room::{RoomHero, RoomInfoNotableUpdateReasons},
338 store::{RoomLoadSettings, StoreConfig},
339 test_utils::logged_in_base_client,
340 };
341
342 #[async_test]
343 async fn test_invited_state_without_update_emits_invited_room() {
344 let client = logged_in_base_client(None).await;
345 let room_id = room_id!("!invite:e.uk");
346 let user_id = client.session_meta().unwrap().user_id.to_owned();
347
348 let mut room = http::response::Room::new();
349 room.invite_state = Some(invite_state_for(&user_id, MembershipState::Invite));
350
351 let response = response_with_room(room_id, room);
352
353 let sync_resp = client
354 .process_sliding_sync(&response, &RequestedRequiredStates::default())
355 .await
356 .unwrap();
357
358 assert!(sync_resp.rooms.invited.contains_key(room_id));
359 }
360
361 use matrix_sdk_common::cross_process_lock::CrossProcessLockConfig;
362 use ruma::events::AnyStrippedStateEvent;
363
364 fn invite_state_for(
365 user_id: &UserId,
366 membership: MembershipState,
367 ) -> Vec<Raw<AnyStrippedStateEvent>> {
368 let content = RoomMemberEventContent::new(membership);
369
370 let raw: Raw<AnyStrippedStateEvent> = Raw::from_json_string(
371 serde_json::json!({
372 "type": "m.room.member",
373 "state_key": user_id,
374 "content": content,
375 })
376 .to_string(),
377 )
378 .unwrap();
379
380 vec![raw]
381 }
382
383 #[async_test]
384 async fn test_knocked_state_emits_invited_room() {
385 let client = logged_in_base_client(None).await;
386 let room_id = room_id!("!knock:e.uk");
387 let user_id = client.session_meta().unwrap().user_id.to_owned();
388
389 let mut room = http::response::Room::new();
390 room.invite_state = Some(invite_state_for(&user_id, MembershipState::Knock));
391
392 let response = response_with_room(room_id, room);
393
394 let sync_resp = client
395 .process_sliding_sync(&response, &RequestedRequiredStates::default())
396 .await
397 .unwrap();
398
399 assert!(sync_resp.rooms.invited.contains_key(room_id));
401 }
402
403 #[async_test]
404 async fn test_notification_count_set() {
405 let client = logged_in_base_client(None).await;
406
407 let mut response = http::Response::new("42".to_owned());
408 let room_id = room_id!("!room:example.org");
409 let count = assign!(UnreadNotificationsCount::default(), {
410 highlight_count: Some(uint!(13)),
411 notification_count: Some(uint!(37)),
412 });
413
414 response.rooms.insert(
415 room_id.to_owned(),
416 assign!(http::response::Room::new(), {
417 unread_notifications: count.clone()
418 }),
419 );
420
421 let sync_response = client
422 .process_sliding_sync(&response, &RequestedRequiredStates::default())
423 .await
424 .expect("Failed to process sync");
425
426 let room = sync_response.rooms.joined.get(room_id).unwrap();
428 assert_eq!(room.unread_notifications, count.clone().into());
429
430 let room = client.get_room(room_id).expect("found room");
432 assert_eq!(room.unread_notification_counts(), count.into());
433 }
434
435 #[async_test]
436 async fn test_can_process_empty_sliding_sync_response() {
437 let client = logged_in_base_client(None).await;
438 let empty_response = http::Response::new("5".to_owned());
439 client
440 .process_sliding_sync(&empty_response, &RequestedRequiredStates::default())
441 .await
442 .expect("Failed to process sync");
443 }
444
445 #[async_test]
446 async fn test_room_with_unspecified_state_is_added_to_client_and_joined_list() {
447 let client = logged_in_base_client(None).await;
449 let room_id = room_id!("!r:e.uk");
450
451 let mut room = http::response::Room::new();
454 room.joined_count = Some(uint!(41));
455 let response = response_with_room(room_id, room);
456 let sync_resp = client
457 .process_sliding_sync(&response, &RequestedRequiredStates::default())
458 .await
459 .expect("Failed to process sync");
460
461 let client_room = client.get_room(room_id).expect("No room found");
463 assert_eq!(client_room.room_id(), room_id);
464 assert_eq!(client_room.joined_members_count(), 41);
465 assert_eq!(client_room.state(), RoomState::Joined);
466
467 assert!(sync_resp.rooms.joined.contains_key(room_id));
469 assert!(!sync_resp.rooms.left.contains_key(room_id));
470 assert!(!sync_resp.rooms.invited.contains_key(room_id));
471 }
472
473 #[async_test]
474 async fn test_missing_room_name_event() {
475 let client = logged_in_base_client(None).await;
477 let room_id = room_id!("!r:e.uk");
478
479 let mut room = http::response::Room::new();
482 room.name = Some("little room".to_owned());
483 let response = response_with_room(room_id, room);
484 let sync_resp = client
485 .process_sliding_sync(&response, &RequestedRequiredStates::default())
486 .await
487 .expect("Failed to process sync");
488
489 let client_room = client.get_room(room_id).expect("No room found");
491 assert!(client_room.name().is_none());
492 assert_eq!(
493 client_room.compute_display_name().await.unwrap().into_inner().to_string(),
494 "Empty Room"
495 );
496 assert_eq!(client_room.state(), RoomState::Joined);
497
498 assert!(sync_resp.rooms.joined.contains_key(room_id));
500 assert!(!sync_resp.rooms.left.contains_key(room_id));
501 assert!(!sync_resp.rooms.invited.contains_key(room_id));
502 assert!(!sync_resp.rooms.knocked.contains_key(room_id));
503 }
504
505 #[async_test]
506 async fn test_room_name_event() {
507 let client = logged_in_base_client(None).await;
509 let room_id = room_id!("!r:e.uk");
510
511 let mut room = http::response::Room::new();
514
515 room.name = Some("little room".to_owned());
516 set_room_name(&mut room, user_id!("@a:b.c"), "The Name".to_owned());
517
518 let response = response_with_room(room_id, room);
519 client
520 .process_sliding_sync(&response, &RequestedRequiredStates::default())
521 .await
522 .expect("Failed to process sync");
523
524 let client_room = client.get_room(room_id).expect("No room found");
526 assert_eq!(client_room.name().as_deref(), Some("The Name"));
527 assert_eq!(
528 client_room.compute_display_name().await.unwrap().into_inner().to_string(),
529 "The Name"
530 );
531 }
532
533 #[async_test]
534 async fn test_missing_invited_room_name_event() {
535 let client = logged_in_base_client(None).await;
537 let room_id = room_id!("!r:e.uk");
538 let user_id = user_id!("@w:e.uk");
539 let inviter = user_id!("@john:mastodon.org");
540
541 let mut room = http::response::Room::new();
544 set_room_invited(&mut room, inviter, user_id);
545 room.name = Some("name from sliding sync response".to_owned());
546 let response = response_with_room(room_id, room);
547 let sync_resp = client
548 .process_sliding_sync(&response, &RequestedRequiredStates::default())
549 .await
550 .expect("Failed to process sync");
551
552 let client_room = client.get_room(room_id).expect("No room found");
554 assert!(client_room.name().is_none());
555
556 assert_eq!(client_room.compute_display_name().await.unwrap().into_inner().to_string(), "w");
558
559 assert_eq!(client_room.state(), RoomState::Invited);
560
561 assert!(!sync_resp.rooms.joined.contains_key(room_id));
563 assert!(!sync_resp.rooms.left.contains_key(room_id));
564 assert!(sync_resp.rooms.invited.contains_key(room_id));
565 assert!(!sync_resp.rooms.knocked.contains_key(room_id));
566 }
567
568 #[async_test]
569 async fn test_invited_room_name_event() {
570 let client = logged_in_base_client(None).await;
572 let room_id = room_id!("!r:e.uk");
573 let user_id = user_id!("@w:e.uk");
574 let inviter = user_id!("@john:mastodon.org");
575
576 let mut room = http::response::Room::new();
579
580 set_room_invited(&mut room, inviter, user_id);
581
582 room.name = Some("name from sliding sync response".to_owned());
583 set_room_name(&mut room, user_id!("@a:b.c"), "The Name".to_owned());
584
585 let response = response_with_room(room_id, room);
586 client
587 .process_sliding_sync(&response, &RequestedRequiredStates::default())
588 .await
589 .expect("Failed to process sync");
590
591 let client_room = client.get_room(room_id).expect("No room found");
593 assert_eq!(client_room.name().as_deref(), Some("The Name"));
594 assert_eq!(
595 client_room.compute_display_name().await.unwrap().into_inner().to_string(),
596 "The Name"
597 );
598 }
599
600 #[async_test]
601 async fn test_receiving_a_knocked_room_membership_event_creates_a_knocked_room() {
602 let client = logged_in_base_client(None).await;
604 let room_id = room_id!("!r:e.uk");
605 let user_id = client.session_meta().unwrap().user_id.to_owned();
606
607 let mut room = http::response::Room::new();
610 set_room_knocked(&mut room, &user_id);
611
612 let response = response_with_room(room_id, room);
613 client
614 .process_sliding_sync(&response, &RequestedRequiredStates::default())
615 .await
616 .expect("Failed to process sync");
617
618 let client_room = client.get_room(room_id).expect("No room found");
620 assert_eq!(client_room.state(), RoomState::Knocked);
621 }
622
623 #[async_test]
624 async fn test_receiving_a_knocked_room_membership_event_with_wrong_state_key_creates_an_invited_room()
625 {
626 let client = logged_in_base_client(None).await;
628 let room_id = room_id!("!r:e.uk");
629 let user_id = user_id!("@w:e.uk");
630
631 let mut room = http::response::Room::new();
633 set_room_knocked(&mut room, user_id);
634
635 let response = response_with_room(room_id, room);
636 client
637 .process_sliding_sync(&response, &RequestedRequiredStates::default())
638 .await
639 .expect("Failed to process sync");
640
641 let client_room = client.get_room(room_id).expect("No room found");
644 assert_eq!(client_room.state(), RoomState::Invited);
645 }
646
647 #[async_test]
648 async fn test_receiving_an_unknown_room_membership_event_in_invite_state_creates_an_invited_room()
649 {
650 let client = logged_in_base_client(None).await;
652 let room_id = room_id!("!r:e.uk");
653 let user_id = client.session_meta().unwrap().user_id.to_owned();
654
655 let mut room = http::response::Room::new();
657 let event = Raw::new(&json!({
658 "type": "m.room.member",
659 "sender": user_id,
660 "content": {
661 "is_direct": true,
662 "membership": "join",
663 },
664 "state_key": user_id,
665 }))
666 .expect("Failed to make raw event")
667 .cast_unchecked();
668 room.invite_state = Some(vec![event]);
669
670 let response = response_with_room(room_id, room);
671 client
672 .process_sliding_sync(&response, &RequestedRequiredStates::default())
673 .await
674 .expect("Failed to process sync");
675
676 let client_room = client.get_room(room_id).expect("No room found");
678 assert_eq!(client_room.state(), RoomState::Invited);
679 }
680
681 #[async_test]
682 async fn test_left_a_room_from_required_state_event() {
683 let client = logged_in_base_client(None).await;
685 let room_id = room_id!("!r:e.uk");
686 let user_id = user_id!("@u:e.uk");
687
688 let mut room = http::response::Room::new();
690 set_room_joined(&mut room, user_id);
691 let response = response_with_room(room_id, room);
692 client
693 .process_sliding_sync(&response, &RequestedRequiredStates::default())
694 .await
695 .expect("Failed to process sync");
696 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Joined);
697
698 let mut room = http::response::Room::new();
700 set_room_left(&mut room, user_id);
701 let response = response_with_room(room_id, room);
702 let sync_resp = client
703 .process_sliding_sync(&response, &RequestedRequiredStates::default())
704 .await
705 .expect("Failed to process sync");
706
707 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Left);
709
710 assert!(!sync_resp.rooms.joined.contains_key(room_id));
712 assert!(sync_resp.rooms.left.contains_key(room_id));
713 assert!(!sync_resp.rooms.invited.contains_key(room_id));
714 assert!(!sync_resp.rooms.knocked.contains_key(room_id));
715 }
716
717 #[async_test]
718 async fn test_kick_or_ban_updates_room_to_left() {
719 for membership in [MembershipState::Leave, MembershipState::Ban] {
720 let room_id = room_id!("!r:e.uk");
721 let user_a_id = user_id!("@a:e.uk");
722 let user_b_id = user_id!("@b:e.uk");
723 let client = logged_in_base_client(Some(user_a_id)).await;
724
725 let mut room = http::response::Room::new();
727 set_room_joined(&mut room, user_a_id);
728 let response = response_with_room(room_id, room);
729 client
730 .process_sliding_sync(&response, &RequestedRequiredStates::default())
731 .await
732 .expect("Failed to process sync");
733 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Joined);
734
735 let mut room = http::response::Room::new();
737 room.required_state.push(make_state_event(
738 user_b_id,
739 user_a_id.as_str(),
740 RoomMemberEventContent::new(membership.clone()),
741 None,
742 ));
743 let response = response_with_room(room_id, room);
744 let sync_resp = client
745 .process_sliding_sync(&response, &RequestedRequiredStates::default())
746 .await
747 .expect("Failed to process sync");
748
749 match membership {
750 MembershipState::Leave => {
751 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Left);
753 }
754 MembershipState::Ban => {
755 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Banned);
757 }
758 _ => panic!("Unexpected membership state found: {membership}"),
759 }
760
761 assert!(!sync_resp.rooms.joined.contains_key(room_id));
763 assert!(sync_resp.rooms.left.contains_key(room_id));
764 assert!(!sync_resp.rooms.invited.contains_key(room_id));
765 assert!(!sync_resp.rooms.knocked.contains_key(room_id));
766 }
767 }
768
769 #[async_test]
770 async fn test_left_a_room_from_timeline_state_event() {
771 let client = logged_in_base_client(None).await;
773 let room_id = room_id!("!r:e.uk");
774 let user_id = user_id!("@u:e.uk");
775
776 let mut room = http::response::Room::new();
778 set_room_joined(&mut room, user_id);
779 let response = response_with_room(room_id, room);
780 client
781 .process_sliding_sync(&response, &RequestedRequiredStates::default())
782 .await
783 .expect("Failed to process sync");
784 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Joined);
785
786 let mut room = http::response::Room::new();
788 set_room_left_as_timeline_event(&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
795 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Joined);
797 }
798
799 #[async_test]
800 async fn test_can_be_reinvited_to_a_left_room() {
801 let client = logged_in_base_client(None).await;
805 let room_id = room_id!("!r:e.uk");
806 let user_id = user_id!("@u:e.uk");
807
808 let mut room = http::response::Room::new();
810 set_room_joined(&mut room, user_id);
811 let response = response_with_room(room_id, room);
812 client
813 .process_sliding_sync(&response, &RequestedRequiredStates::default())
814 .await
815 .expect("Failed to process sync");
816 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Joined);
818
819 let mut room = http::response::Room::new();
821 set_room_left(&mut room, user_id);
822 let response = response_with_room(room_id, room);
823 client
824 .process_sliding_sync(&response, &RequestedRequiredStates::default())
825 .await
826 .expect("Failed to process sync");
827 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Left);
829
830 let mut room = http::response::Room::new();
832 set_room_invited(&mut room, user_id, user_id);
833 let response = response_with_room(room_id, room);
834 client
835 .process_sliding_sync(&response, &RequestedRequiredStates::default())
836 .await
837 .expect("Failed to process sync");
838
839 assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Invited);
841 }
842
843 #[async_test]
844 async fn test_other_person_leaving_a_dm_is_reflected_in_their_membership_and_direct_targets() {
845 let room_id = room_id!("!r:e.uk");
846 let user_a_id = user_id!("@a:e.uk");
847 let user_b_id = user_id!("@b:e.uk");
848
849 let client = logged_in_base_client(None).await;
851 create_dm(&client, room_id, user_a_id, user_b_id, MembershipState::Join).await;
852
853 assert!(
855 direct_targets(&client, room_id).contains(<&DirectUserIdentifier>::from(user_b_id))
856 );
857 assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Join);
858
859 update_room_membership(&client, room_id, user_b_id, MembershipState::Leave).await;
861
862 assert!(
866 direct_targets(&client, room_id).contains(<&DirectUserIdentifier>::from(user_b_id))
867 );
868 assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Leave);
869 }
870
871 #[async_test]
872 async fn test_other_person_refusing_invite_to_a_dm_is_reflected_in_their_membership_and_direct_targets()
873 {
874 let room_id = room_id!("!r:e.uk");
875 let user_a_id = user_id!("@a:e.uk");
876 let user_b_id = user_id!("@b:e.uk");
877
878 let client = logged_in_base_client(None).await;
880 create_dm(&client, room_id, user_a_id, user_b_id, MembershipState::Invite).await;
881
882 assert!(
884 direct_targets(&client, room_id).contains(<&DirectUserIdentifier>::from(user_b_id))
885 );
886 assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Invite);
887
888 update_room_membership(&client, room_id, user_b_id, MembershipState::Leave).await;
890
891 assert!(
895 direct_targets(&client, room_id).contains(<&DirectUserIdentifier>::from(user_b_id))
896 );
897 assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Leave);
898 }
899
900 #[async_test]
901 async fn test_members_count_in_a_dm_where_other_person_has_joined() {
902 let room_id = room_id!("!r:bar.org");
903 let user_a_id = user_id!("@a:bar.org");
904 let user_b_id = user_id!("@b:bar.org");
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_eq!(membership(&client, room_id, user_a_id).await, MembershipState::Join);
912
913 assert!(
915 direct_targets(&client, room_id).contains(<&DirectUserIdentifier>::from(user_b_id))
916 );
917 assert_eq!(membership(&client, room_id, user_b_id).await, MembershipState::Join);
918
919 let room = client.get_room(room_id).unwrap();
920
921 assert_eq!(room.active_members_count(), 2);
922 assert_eq!(room.joined_members_count(), 2);
923 assert_eq!(room.invited_members_count(), 0);
924 }
925
926 #[async_test]
927 async fn test_members_count_in_a_dm_where_other_person_is_invited() {
928 let room_id = room_id!("!r:bar.org");
929 let user_a_id = user_id!("@a:bar.org");
930 let user_b_id = user_id!("@b:bar.org");
931
932 let client = logged_in_base_client(None).await;
934 create_dm(&client, room_id, user_a_id, user_b_id, MembershipState::Invite).await;
935
936 assert_eq!(membership(&client, room_id, user_a_id).await, MembershipState::Join);
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 let room = client.get_room(room_id).unwrap();
946
947 assert_eq!(room.active_members_count(), 2);
948 assert_eq!(room.joined_members_count(), 1);
949 assert_eq!(room.invited_members_count(), 1);
950 }
951
952 #[async_test]
953 async fn test_avatar_is_found_when_processing_sliding_sync_response() {
954 let client = logged_in_base_client(None).await;
956 let room_id = room_id!("!r:e.uk");
957
958 let room = {
960 let mut room = http::response::Room::new();
961 room.avatar = JsOption::from_option(Some(owned_mxc_uri!("mxc://e.uk/med1")));
962
963 room
964 };
965 let response = response_with_room(room_id, room);
966 client
967 .process_sliding_sync(&response, &RequestedRequiredStates::default())
968 .await
969 .expect("Failed to process sync");
970
971 let client_room = client.get_room(room_id).expect("No room found");
973 assert_eq!(
974 client_room.avatar_url().expect("No avatar URL").media_id().expect("No media ID"),
975 "med1"
976 );
977 }
978
979 #[async_test]
980 async fn test_avatar_can_be_unset_when_processing_sliding_sync_response() {
981 let client = logged_in_base_client(None).await;
983 let room_id = room_id!("!r:e.uk");
984
985 let room = {
989 let mut room = http::response::Room::new();
990 room.avatar = JsOption::from_option(Some(owned_mxc_uri!("mxc://e.uk/med1")));
991
992 room
993 };
994 let response = response_with_room(room_id, room);
995 client
996 .process_sliding_sync(&response, &RequestedRequiredStates::default())
997 .await
998 .expect("Failed to process sync");
999
1000 let client_room = client.get_room(room_id).expect("No room found");
1002 assert_eq!(
1003 client_room.avatar_url().expect("No avatar URL").media_id().expect("No media ID"),
1004 "med1"
1005 );
1006
1007 let room = http::response::Room::new();
1011 let response = response_with_room(room_id, room);
1012 client
1013 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1014 .await
1015 .expect("Failed to process sync");
1016
1017 let client_room = client.get_room(room_id).expect("No room found");
1019 assert_eq!(
1020 client_room.avatar_url().expect("No avatar URL").media_id().expect("No media ID"),
1021 "med1"
1022 );
1023
1024 let room = {
1028 let mut room = http::response::Room::new();
1029 room.avatar = JsOption::Null;
1030
1031 room
1032 };
1033 let response = response_with_room(room_id, room);
1034 client
1035 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1036 .await
1037 .expect("Failed to process sync");
1038
1039 let client_room = client.get_room(room_id).expect("No room found");
1041 assert!(client_room.avatar_url().is_none());
1042 }
1043
1044 #[async_test]
1045 async fn test_avatar_is_found_from_required_state_when_processing_sliding_sync_response() {
1046 let client = logged_in_base_client(None).await;
1048 let room_id = room_id!("!r:e.uk");
1049 let user_id = user_id!("@u:e.uk");
1050
1051 let room = room_with_avatar(mxc_uri!("mxc://e.uk/med1"), user_id);
1053 let response = response_with_room(room_id, room);
1054 client
1055 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1056 .await
1057 .expect("Failed to process sync");
1058
1059 let client_room = client.get_room(room_id).expect("No room found");
1061 assert_eq!(
1062 client_room.avatar_url().expect("No avatar URL").media_id().expect("No media ID"),
1063 "med1"
1064 );
1065 }
1066
1067 #[async_test]
1068 async fn test_invitation_room_is_added_to_client_and_invite_list() {
1069 let client = logged_in_base_client(None).await;
1071 let room_id = room_id!("!r:e.uk");
1072 let user_id = user_id!("@u:e.uk");
1073
1074 let mut room_info_notable_update = client.room_info_notable_update_receiver();
1075
1076 let mut room = http::response::Room::new();
1078 set_room_invited(&mut room, user_id, user_id);
1079 let response = response_with_room(room_id, room);
1080 let sync_resp = client
1081 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1082 .await
1083 .expect("Failed to process sync");
1084
1085 let client_room = client.get_room(room_id).expect("No room found");
1087 assert_eq!(client_room.room_id(), room_id);
1088 assert_eq!(client_room.state(), RoomState::Invited);
1089
1090 assert!(!sync_resp.rooms.invited[room_id].invite_state.is_empty());
1092 assert!(!sync_resp.rooms.joined.contains_key(room_id));
1093
1094 assert_matches!(
1095 room_info_notable_update.recv().await,
1096 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons }) => {
1097 assert_eq!(received_room_id, room_id);
1098 assert!(reasons.contains(RoomInfoNotableUpdateReasons::MEMBERSHIP));
1100 }
1101 );
1102 assert_matches!(
1103 room_info_notable_update.recv().await,
1104 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons }) => {
1105 assert_eq!(received_room_id, room_id);
1106 assert!(reasons.contains(RoomInfoNotableUpdateReasons::DISPLAY_NAME));
1108 }
1109 );
1110 assert!(room_info_notable_update.is_empty());
1111 }
1112
1113 #[async_test]
1114 async fn test_knock_room_is_added_to_client_and_knock_list() {
1115 let client = logged_in_base_client(None).await;
1117 let room_id = room_id!("!r:e.uk");
1118 let user_id = user_id!("@u:e.uk");
1119
1120 let mut room_info_notable_update = client.room_info_notable_update_receiver();
1121
1122 let mut room = http::response::Room::new();
1124 set_room_knocked(&mut room, user_id);
1125 let response = response_with_room(room_id, room);
1126 let sync_resp = client
1127 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1128 .await
1129 .expect("Failed to process sync");
1130
1131 let client_room = client.get_room(room_id).expect("No room found");
1133 assert_eq!(client_room.room_id(), room_id);
1134 assert_eq!(client_room.state(), RoomState::Knocked);
1135
1136 assert!(!sync_resp.rooms.knocked[room_id].knock_state.is_empty());
1138 assert!(!sync_resp.rooms.joined.contains_key(room_id));
1139
1140 assert_matches!(
1141 room_info_notable_update.recv().await,
1142 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons }) => {
1143 assert_eq!(received_room_id, room_id);
1144 assert!(reasons.contains(RoomInfoNotableUpdateReasons::MEMBERSHIP));
1146 }
1147 );
1148 assert_matches!(
1149 room_info_notable_update.recv().await,
1150 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons }) => {
1151 assert_eq!(received_room_id, room_id);
1152 assert!(reasons.contains(RoomInfoNotableUpdateReasons::DISPLAY_NAME));
1154 }
1155 );
1156 assert!(room_info_notable_update.is_empty());
1157 }
1158
1159 #[async_test]
1160 async fn test_avatar_is_found_in_invitation_room_when_processing_sliding_sync_response() {
1161 let client = logged_in_base_client(None).await;
1163 let room_id = room_id!("!r:e.uk");
1164 let user_id = user_id!("@u:e.uk");
1165
1166 let mut room = room_with_avatar(mxc_uri!("mxc://e.uk/med1"), user_id);
1168 set_room_invited(&mut room, user_id, user_id);
1169 let response = response_with_room(room_id, room);
1170 client
1171 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1172 .await
1173 .expect("Failed to process sync");
1174
1175 let client_room = client.get_room(room_id).expect("No room found");
1177 assert_eq!(
1178 client_room.avatar_url().expect("No avatar URL").media_id().expect("No media ID"),
1179 "med1"
1180 );
1181 }
1182
1183 #[async_test]
1184 async fn test_canonical_alias_is_found_in_invitation_room_when_processing_sliding_sync_response()
1185 {
1186 let client = logged_in_base_client(None).await;
1188 let room_id = room_id!("!r:e.uk");
1189 let user_id = user_id!("@u:e.uk");
1190 let room_alias_id = room_alias_id!("#myroom:e.uk");
1191
1192 let mut room = room_with_canonical_alias(room_alias_id, user_id);
1194 set_room_invited(&mut room, user_id, user_id);
1195 let response = response_with_room(room_id, room);
1196 client
1197 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1198 .await
1199 .expect("Failed to process sync");
1200
1201 let client_room = client.get_room(room_id).expect("No room found");
1203 assert_eq!(client_room.canonical_alias(), Some(room_alias_id.to_owned()));
1204 }
1205
1206 #[async_test]
1207 async fn test_display_name_from_sliding_sync_doesnt_override_alias() {
1208 let client = logged_in_base_client(None).await;
1210 let room_id = room_id!("!r:e.uk");
1211 let user_id = user_id!("@u:e.uk");
1212 let room_alias_id = room_alias_id!("#myroom:e.uk");
1213
1214 let mut room = room_with_canonical_alias(room_alias_id, user_id);
1217 room.name = Some("This came from the server".to_owned());
1218 let response = response_with_room(room_id, room);
1219 client
1220 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1221 .await
1222 .expect("Failed to process sync");
1223
1224 let client_room = client.get_room(room_id).expect("No room found");
1226 assert_eq!(
1227 client_room.compute_display_name().await.unwrap().into_inner().to_string(),
1228 "myroom"
1229 );
1230 assert!(client_room.name().is_none());
1231 }
1232
1233 #[async_test]
1234 async fn test_display_name_is_cached_and_emits_a_notable_update_reason() {
1235 let client = logged_in_base_client(None).await;
1236 let user_id = user_id!("@u:e.uk");
1237 let room_id = room_id!("!r:e.uk");
1238
1239 let mut room_info_notable_update = client.room_info_notable_update_receiver();
1240
1241 let room = room_with_name("Hello World", user_id);
1242 let response = response_with_room(room_id, room);
1243 client
1244 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1245 .await
1246 .expect("Failed to process sync");
1247
1248 let room = client.get_room(room_id).expect("No room found");
1249 assert_eq!(room.cached_display_name().unwrap().to_string(), "Hello World");
1250
1251 assert_matches!(
1252 room_info_notable_update.recv().await,
1253 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons }) => {
1254 assert_eq!(received_room_id, room_id);
1255 assert!(reasons.contains(RoomInfoNotableUpdateReasons::NONE));
1256 }
1257 );
1258 assert_matches!(
1259 room_info_notable_update.recv().await,
1260 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons }) => {
1261 assert_eq!(received_room_id, room_id);
1262 assert!(reasons.contains(RoomInfoNotableUpdateReasons::DISPLAY_NAME));
1264 }
1265 );
1266 assert!(room_info_notable_update.is_empty());
1267 }
1268
1269 #[async_test]
1270 async fn test_display_name_is_persisted_from_sliding_sync() {
1271 let user_id = user_id!("@u:e.uk");
1272 let room_id = room_id!("!r:e.uk");
1273 let session_meta = SessionMeta { user_id: user_id.to_owned(), device_id: "FOOBAR".into() };
1274 let state_store;
1275
1276 {
1277 let client = {
1278 let store = StoreConfig::new(CrossProcessLockConfig::SingleProcess);
1279 state_store = store.state_store.clone();
1280
1281 let client = BaseClient::new(store, ThreadingSupport::Disabled);
1282 client
1283 .activate(
1284 session_meta.clone(),
1285 RoomLoadSettings::default(),
1286 #[cfg(feature = "e2e-encryption")]
1287 None,
1288 )
1289 .await
1290 .expect("`activate` failed!");
1291
1292 client
1293 };
1294
1295 let room = room_with_name("Hello World", user_id);
1298 let response = response_with_room(room_id, room);
1299 client
1300 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1301 .await
1302 .expect("Failed to process sync");
1303
1304 let room = client.get_room(room_id).expect("No room found");
1305 assert_eq!(room.cached_display_name().unwrap().to_string(), "Hello World");
1306 }
1307
1308 {
1309 let client = {
1310 let mut store = StoreConfig::new(CrossProcessLockConfig::SingleProcess);
1311 store.state_store = state_store;
1312 let client = BaseClient::new(store, ThreadingSupport::Disabled);
1313 client
1314 .activate(
1315 session_meta,
1316 RoomLoadSettings::default(),
1317 #[cfg(feature = "e2e-encryption")]
1318 None,
1319 )
1320 .await
1321 .expect("`activate` failed!");
1322
1323 client
1324 };
1325
1326 let room = client.get_room(room_id).expect("No room found");
1327 assert_eq!(room.cached_display_name().unwrap().to_string(), "Hello World");
1328 }
1329 }
1330
1331 #[async_test]
1332 async fn test_compute_heroes_from_sliding_sync() {
1333 let client = logged_in_base_client(None).await;
1335 let room_id = room_id!("!r:e.uk");
1336 let gordon = owned_user_id!("@gordon:e.uk");
1337 let alice = owned_user_id!("@alice:e.uk");
1338
1339 let mut room = http::response::Room::new();
1342 room.heroes = Some(vec![
1343 assign!(http::response::Hero::new(gordon), {
1344 name: Some("Gordon".to_owned()),
1345 }),
1346 assign!(http::response::Hero::new(alice), {
1347 name: Some("Alice".to_owned()),
1348 avatar: Some(owned_mxc_uri!("mxc://e.uk/med1"))
1349 }),
1350 ]);
1351 let response = response_with_room(room_id, room);
1352 let _sync_resp = client
1353 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1354 .await
1355 .expect("Failed to process sync");
1356
1357 let client_room = client.get_room(room_id).expect("No room found");
1359 assert_eq!(client_room.room_id(), room_id);
1360 assert_eq!(client_room.state(), RoomState::Joined);
1361
1362 assert_eq!(
1364 client_room.clone_info().summary.heroes(),
1365 &[
1366 RoomHero {
1367 user_id: owned_user_id!("@gordon:e.uk"),
1368 display_name: Some("Gordon".to_owned()),
1369 avatar_url: None
1370 },
1371 RoomHero {
1372 user_id: owned_user_id!("@alice:e.uk"),
1373 display_name: Some("Alice".to_owned()),
1374 avatar_url: Some(owned_mxc_uri!("mxc://e.uk/med1"))
1375 },
1376 ]
1377 );
1378 }
1379
1380 #[async_test]
1381 async fn test_recency_stamp_is_found_when_processing_sliding_sync_response() {
1382 let client = logged_in_base_client(None).await;
1384 let room_id = room_id!("!r:e.uk");
1385
1386 let room = assign!(http::response::Room::new(), {
1388 bump_stamp: Some(42u32.into()),
1389 });
1390 let response = response_with_room(room_id, room);
1391 client
1392 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1393 .await
1394 .expect("Failed to process sync");
1395
1396 let client_room = client.get_room(room_id).expect("No room found");
1398 assert_eq!(client_room.recency_stamp().expect("No recency stamp"), 42.into());
1399 }
1400
1401 #[async_test]
1402 async fn test_recency_stamp_can_be_overwritten_when_present_in_a_sliding_sync_response() {
1403 let client = logged_in_base_client(None).await;
1405 let room_id = room_id!("!r:e.uk");
1406
1407 {
1408 let room = assign!(http::response::Room::new(), {
1410 bump_stamp: Some(42u32.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"), 42.into());
1421 }
1422
1423 {
1424 let room = assign!(http::response::Room::new(), {
1426 bump_stamp: None,
1427 });
1428 let response = response_with_room(room_id, room);
1429 client
1430 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1431 .await
1432 .expect("Failed to process sync");
1433
1434 let client_room = client.get_room(room_id).expect("No room found");
1436 assert_eq!(client_room.recency_stamp().expect("No recency stamp"), 42.into());
1437 }
1438
1439 {
1440 let room = assign!(http::response::Room::new(), {
1443 bump_stamp: Some(153u32.into()),
1444 });
1445 let response = response_with_room(room_id, room);
1446 client
1447 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1448 .await
1449 .expect("Failed to process sync");
1450
1451 let client_room = client.get_room(room_id).expect("No room found");
1453 assert_eq!(client_room.recency_stamp().expect("No recency stamp"), 153.into());
1454 }
1455 }
1456
1457 #[async_test]
1458 async fn test_recency_stamp_can_trigger_a_notable_update_reason() {
1459 let client = logged_in_base_client(None).await;
1461 let mut room_info_notable_update_stream = client.room_info_notable_update_receiver();
1462 let room_id = room_id!("!r:e.uk");
1463
1464 let room = assign!(http::response::Room::new(), {
1466 bump_stamp: Some(42u32.into()),
1467 });
1468 let response = response_with_room(room_id, room);
1469 client
1470 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1471 .await
1472 .expect("Failed to process sync");
1473
1474 assert_matches!(
1477 room_info_notable_update_stream.recv().await,
1478 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1479 assert_eq!(received_room_id, room_id);
1480 assert!(!received_reasons.contains(RoomInfoNotableUpdateReasons::RECENCY_STAMP));
1481 }
1482 );
1483 assert_matches!(
1484 room_info_notable_update_stream.recv().await,
1485 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1486 assert_eq!(received_room_id, room_id);
1487 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::DISPLAY_NAME));
1488 }
1489 );
1490 assert!(room_info_notable_update_stream.is_empty());
1491
1492 let room = assign!(http::response::Room::new(), {
1494 bump_stamp: Some(43u32.into()),
1495 });
1496 let response = response_with_room(room_id, room);
1497 client
1498 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1499 .await
1500 .expect("Failed to process sync");
1501
1502 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::RECENCY_STAMP));
1508 }
1509 );
1510 assert!(room_info_notable_update_stream.is_empty());
1511 }
1512
1513 #[async_test]
1514 async fn test_leaving_room_can_trigger_a_notable_update_reason() {
1515 let client = logged_in_base_client(None).await;
1517 let mut room_info_notable_update_stream = client.room_info_notable_update_receiver();
1518
1519 let room_id = room_id!("!r:e.uk");
1521 let room = http::response::Room::new();
1522 let response = response_with_room(room_id, room);
1523 client
1524 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1525 .await
1526 .expect("Failed to process sync");
1527
1528 assert_matches!(
1530 room_info_notable_update_stream.recv().await,
1531 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1532 assert_eq!(received_room_id, room_id);
1533 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::NONE));
1534 }
1535 );
1536 assert_matches!(
1537 room_info_notable_update_stream.recv().await,
1538 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1539 assert_eq!(received_room_id, room_id);
1540 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::DISPLAY_NAME));
1541 }
1542 );
1543
1544 let room_id = room_id!("!r:e.uk");
1546 let events = vec![
1547 Raw::from_json_string(
1548 json!({
1549 "type": "m.room.member",
1550 "event_id": "$3",
1551 "content": { "membership": "join" },
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::NONE));
1575 }
1576 );
1577 assert!(room_info_notable_update_stream.is_empty());
1578
1579 let events = vec![
1580 Raw::from_json_string(
1581 json!({
1582 "type": "m.room.member",
1583 "event_id": "$3",
1584 "content": { "membership": "leave" },
1585 "sender": "@u:h.uk",
1586 "origin_server_ts": 12344445,
1587 "state_key": "@u:e.uk",
1588 })
1589 .to_string(),
1590 )
1591 .unwrap(),
1592 ];
1593 let room = assign!(http::response::Room::new(), {
1594 required_state: events,
1595 });
1596 let response = response_with_room(room_id, room);
1597 client
1598 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1599 .await
1600 .expect("Failed to process sync");
1601
1602 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::MEMBERSHIP));
1608 }
1609 );
1610 assert!(room_info_notable_update_stream.is_empty());
1611 }
1612
1613 #[async_test]
1614 async fn test_unread_marker_can_trigger_a_notable_update_reason() {
1615 let client = logged_in_base_client(None).await;
1617 let mut room_info_notable_update_stream = client.room_info_notable_update_receiver();
1618
1619 let room_id = room_id!("!r:e.uk");
1621 let room = http::response::Room::new();
1622 let response = response_with_room(room_id, room);
1623 client
1624 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1625 .await
1626 .expect("Failed to process sync");
1627
1628 assert_matches!(
1630 room_info_notable_update_stream.recv().await,
1631 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1632 assert_eq!(received_room_id, room_id);
1633 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::NONE), "{received_reasons:?}");
1634 }
1635 );
1636 assert_matches!(
1637 room_info_notable_update_stream.recv().await,
1638 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1639 assert_eq!(received_room_id, room_id);
1640 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::DISPLAY_NAME), "{received_reasons:?}");
1641 }
1642 );
1643 assert!(room_info_notable_update_stream.is_empty());
1644
1645 let room_id = room_id!("!r:e.uk");
1648 let room_account_data_events = vec![
1649 Raw::from_json_string(
1650 json!({
1651 "type": "m.marked_unread",
1652 "event_id": "$1",
1653 "content": { "unread": true },
1654 "sender": client.session_meta().unwrap().user_id,
1655 "origin_server_ts": 12344445,
1656 })
1657 .to_string(),
1658 )
1659 .unwrap(),
1660 ];
1661 let mut response = response_with_room(room_id, http::response::Room::new());
1662 response.extensions.account_data.rooms.insert(room_id.to_owned(), room_account_data_events);
1663
1664 client
1665 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1666 .await
1667 .expect("Failed to process sync");
1668
1669 assert_matches!(
1671 room_info_notable_update_stream.recv().await,
1672 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1673 assert_eq!(received_room_id, room_id);
1674 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::UNREAD_MARKER), "{received_reasons:?}");
1675 }
1676 );
1677
1678 client
1680 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1681 .await
1682 .expect("Failed to process sync");
1683
1684 assert_matches!(
1685 room_info_notable_update_stream.recv().await,
1686 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1687 assert_eq!(received_room_id, room_id);
1688 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::NONE), "{received_reasons:?}");
1689 }
1690 );
1691 assert!(room_info_notable_update_stream.is_empty());
1692
1693 let room_account_data_events = vec![
1695 Raw::from_json_string(
1696 json!({
1697 "type": "m.marked_unread",
1698 "event_id": "$1",
1699 "content": { "unread": false },
1700 "sender": client.session_meta().unwrap().user_id,
1701 "origin_server_ts": 12344445,
1702 })
1703 .to_string(),
1704 )
1705 .unwrap(),
1706 ];
1707 response.extensions.account_data.rooms.insert(room_id.to_owned(), room_account_data_events);
1708 client
1709 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1710 .await
1711 .expect("Failed to process sync");
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::UNREAD_MARKER));
1718 }
1719 );
1720 assert!(room_info_notable_update_stream.is_empty());
1721 }
1722
1723 #[async_test]
1724 async fn test_unstable_unread_marker_is_ignored_after_stable() {
1725 let client = logged_in_base_client(None).await;
1727 let mut room_info_notable_update_stream = client.room_info_notable_update_receiver();
1728
1729 let room_id = room_id!("!r:e.uk");
1731 let room = http::response::Room::new();
1732 let response = response_with_room(room_id, room);
1733 client
1734 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1735 .await
1736 .expect("Failed to process sync");
1737
1738 assert_matches!(
1740 room_info_notable_update_stream.recv().await,
1741 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1742 assert_eq!(received_room_id, room_id);
1743 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::NONE), "{received_reasons:?}");
1744 }
1745 );
1746 assert_matches!(
1747 room_info_notable_update_stream.recv().await,
1748 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1749 assert_eq!(received_room_id, room_id);
1750 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::DISPLAY_NAME), "{received_reasons:?}");
1751 }
1752 );
1753 assert!(room_info_notable_update_stream.is_empty());
1754
1755 let room_id = room_id!("!r:e.uk");
1758 let unstable_room_account_data_events = vec![
1759 Raw::from_json_string(
1760 json!({
1761 "type": "com.famedly.marked_unread",
1762 "event_id": "$1",
1763 "content": { "unread": true },
1764 "sender": client.session_meta().unwrap().user_id,
1765 "origin_server_ts": 12344445,
1766 })
1767 .to_string(),
1768 )
1769 .unwrap(),
1770 ];
1771 let mut response = response_with_room(room_id, http::response::Room::new());
1772 response
1773 .extensions
1774 .account_data
1775 .rooms
1776 .insert(room_id.to_owned(), unstable_room_account_data_events.clone());
1777
1778 client
1779 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1780 .await
1781 .expect("Failed to process sync");
1782
1783 assert_matches!(
1785 room_info_notable_update_stream.recv().await,
1786 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1787 assert_eq!(received_room_id, room_id);
1788 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::UNREAD_MARKER), "{received_reasons:?}");
1789 }
1790 );
1791 assert!(room_info_notable_update_stream.is_empty());
1792
1793 let stable_room_account_data_events = vec![
1795 Raw::from_json_string(
1796 json!({
1797 "type": "m.marked_unread",
1798 "event_id": "$1",
1799 "content": { "unread": false },
1800 "sender": client.session_meta().unwrap().user_id,
1801 "origin_server_ts": 12344445,
1802 })
1803 .to_string(),
1804 )
1805 .unwrap(),
1806 ];
1807 response
1808 .extensions
1809 .account_data
1810 .rooms
1811 .insert(room_id.to_owned(), stable_room_account_data_events);
1812 client
1813 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1814 .await
1815 .expect("Failed to process sync");
1816
1817 assert_matches!(
1819 room_info_notable_update_stream.recv().await,
1820 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1821 assert_eq!(received_room_id, room_id);
1822 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::UNREAD_MARKER));
1823 }
1824 );
1825 assert!(room_info_notable_update_stream.is_empty());
1826
1827 response
1830 .extensions
1831 .account_data
1832 .rooms
1833 .insert(room_id.to_owned(), unstable_room_account_data_events);
1834 client
1835 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1836 .await
1837 .expect("Failed to process sync");
1838
1839 assert_matches!(
1841 room_info_notable_update_stream.recv().await,
1842 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1843 assert_eq!(received_room_id, room_id);
1844 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::NONE), "{received_reasons:?}");
1845 }
1846 );
1847 assert!(room_info_notable_update_stream.is_empty());
1848
1849 let stable_room_account_data_events = vec![
1852 Raw::from_json_string(
1853 json!({
1854 "type": "m.marked_unread",
1855 "event_id": "$3",
1856 "content": { "unread": true },
1857 "sender": client.session_meta().unwrap().user_id,
1858 "origin_server_ts": 12344445,
1859 })
1860 .to_string(),
1861 )
1862 .unwrap(),
1863 ];
1864 response
1865 .extensions
1866 .account_data
1867 .rooms
1868 .insert(room_id.to_owned(), stable_room_account_data_events);
1869 client
1870 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1871 .await
1872 .expect("Failed to process sync");
1873
1874 assert_matches!(
1876 room_info_notable_update_stream.recv().await,
1877 Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons: received_reasons }) => {
1878 assert_eq!(received_room_id, room_id);
1879 assert!(received_reasons.contains(RoomInfoNotableUpdateReasons::UNREAD_MARKER));
1880 }
1881 );
1882 assert!(room_info_notable_update_stream.is_empty());
1883 }
1884
1885 #[async_test]
1886 async fn test_pinned_events_are_updated_on_sync() {
1887 let user_a_id = user_id!("@a:e.uk");
1888 let client = logged_in_base_client(Some(user_a_id)).await;
1889 let room_id = room_id!("!r:e.uk");
1890 let pinned_event_id = owned_event_id!("$an-id:e.uk");
1891
1892 let mut room_response = http::response::Room::new();
1894 set_room_joined(&mut room_response, user_a_id);
1895 let response = response_with_room(room_id, room_response);
1896 client
1897 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1898 .await
1899 .expect("Failed to process sync");
1900
1901 let room = client.get_room(room_id).unwrap();
1903 let pinned_event_ids = room.pinned_event_ids();
1904 assert_matches!(pinned_event_ids, None);
1905
1906 let mut room_response = http::response::Room::new();
1908 room_response.required_state.push(make_state_event(
1909 user_a_id,
1910 "",
1911 RoomPinnedEventsEventContent::new(vec![pinned_event_id.clone()]),
1912 None,
1913 ));
1914 let response = response_with_room(room_id, room_response);
1915 client
1916 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1917 .await
1918 .expect("Failed to process sync");
1919
1920 let pinned_event_ids = room.pinned_event_ids().unwrap_or_default();
1921 assert_eq!(pinned_event_ids.len(), 1);
1922 assert_eq!(pinned_event_ids[0], pinned_event_id);
1923
1924 let mut room_response = http::response::Room::new();
1926 room_response.required_state.push(make_state_event(
1927 user_a_id,
1928 "",
1929 RoomPinnedEventsEventContent::new(Vec::new()),
1930 None,
1931 ));
1932 let response = response_with_room(room_id, room_response);
1933 client
1934 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1935 .await
1936 .expect("Failed to process sync");
1937 let pinned_event_ids = room.pinned_event_ids().unwrap();
1938 assert!(pinned_event_ids.is_empty());
1939 }
1940
1941 #[async_test]
1942 async fn test_dms_are_processed_in_any_sync_response() {
1943 let current_user_id = user_id!("@current:e.uk");
1944 let client = logged_in_base_client(Some(current_user_id)).await;
1945 let user_a_id = user_id!("@a:e.uk");
1946 let user_b_id = user_id!("@b:e.uk");
1947 let room_id_1 = room_id!("!r:e.uk");
1948 let room_id_2 = room_id!("!s:e.uk");
1949
1950 let mut room_response = http::response::Room::new();
1951 set_room_joined(&mut room_response, user_a_id);
1952 let mut response = response_with_room(room_id_1, room_response);
1953 let mut direct_content: BTreeMap<OwnedDirectUserIdentifier, Vec<OwnedRoomId>> =
1954 BTreeMap::new();
1955 direct_content.insert(user_a_id.into(), vec![room_id_1.to_owned()]);
1956 direct_content.insert(user_b_id.into(), vec![room_id_2.to_owned()]);
1957 response
1958 .extensions
1959 .account_data
1960 .global
1961 .push(make_global_account_data_event(DirectEventContent(direct_content)));
1962 client
1963 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1964 .await
1965 .expect("Failed to process sync");
1966
1967 let room_1 = client.get_room(room_id_1).unwrap();
1968 assert!(room_1.is_direct().await.unwrap());
1969
1970 let mut room_response = http::response::Room::new();
1972 set_room_joined(&mut room_response, user_b_id);
1973 let response = response_with_room(room_id_2, room_response);
1974 client
1975 .process_sliding_sync(&response, &RequestedRequiredStates::default())
1976 .await
1977 .expect("Failed to process sync");
1978
1979 let room_2 = client.get_room(room_id_2).unwrap();
1980 assert!(room_2.is_direct().await.unwrap());
1981 }
1982
1983 #[async_test]
1984 async fn test_room_encryption_state_is_and_is_not_encrypted() {
1985 let user_id = user_id!("@raclette:patate");
1986 let client = logged_in_base_client(Some(user_id)).await;
1987 let room_id_0 = room_id!("!r0");
1988 let room_id_1 = room_id!("!r1");
1989 let room_id_2 = room_id!("!r2");
1990
1991 let requested_required_states = RequestedRequiredStates::from(&{
2008 let mut request = http::Request::new();
2009
2010 request.room_subscriptions.insert(room_id_0.to_owned(), {
2011 let mut room_subscription = http::request::RoomSubscription::default();
2012
2013 room_subscription
2014 .required_state
2015 .push((StateEventType::RoomEncryption, "".to_owned()));
2016
2017 room_subscription
2018 });
2019
2020 request
2021 });
2022
2023 let mut response = http::Response::new("0".to_owned());
2024
2025 {
2029 let not_encrypted_room = http::response::Room::new();
2030 let mut encrypted_room = http::response::Room::new();
2031 set_room_is_encrypted(&mut encrypted_room, user_id);
2032
2033 response.rooms.insert(room_id_0.to_owned(), encrypted_room.clone());
2034 response.rooms.insert(room_id_1.to_owned(), encrypted_room);
2035 response.rooms.insert(room_id_2.to_owned(), not_encrypted_room);
2036 }
2037
2038 client
2039 .process_sliding_sync(&response, &requested_required_states)
2040 .await
2041 .expect("Failed to process sync");
2042
2043 assert_matches!(
2045 client.get_room(room_id_0).unwrap().encryption_state(),
2046 EncryptionState::Encrypted
2047 );
2048 assert_matches!(
2049 client.get_room(room_id_1).unwrap().encryption_state(),
2050 EncryptionState::Encrypted
2051 );
2052 assert_matches!(
2054 client.get_room(room_id_2).unwrap().encryption_state(),
2055 EncryptionState::NotEncrypted
2056 )
2057 }
2058
2059 #[async_test]
2060 async fn test_room_encryption_state_is_unknown() {
2061 let user_id = user_id!("@raclette:patate");
2062 let client = logged_in_base_client(Some(user_id)).await;
2063 let room_id_0 = room_id!("!r0");
2064 let room_id_1 = room_id!("!r1");
2065
2066 let requested_required_states = RequestedRequiredStates::from(&http::Request::new());
2079
2080 let mut response = http::Response::new("0".to_owned());
2081
2082 {
2084 let not_encrypted_room = http::response::Room::new();
2085 let mut encrypted_room = http::response::Room::new();
2086 set_room_is_encrypted(&mut encrypted_room, user_id);
2087
2088 response.rooms.insert(room_id_0.to_owned(), encrypted_room);
2089 response.rooms.insert(room_id_1.to_owned(), not_encrypted_room);
2090 }
2091
2092 client
2093 .process_sliding_sync(&response, &requested_required_states)
2094 .await
2095 .expect("Failed to process sync");
2096
2097 assert_matches!(
2100 client.get_room(room_id_0).unwrap().encryption_state(),
2101 EncryptionState::Encrypted
2102 );
2103 assert_matches!(
2106 client.get_room(room_id_1).unwrap().encryption_state(),
2107 EncryptionState::Unknown
2108 );
2109 }
2110
2111 async fn membership(
2112 client: &BaseClient,
2113 room_id: &RoomId,
2114 user_id: &UserId,
2115 ) -> MembershipState {
2116 let room = client.get_room(room_id).expect("Room not found!");
2117 let member = room.get_member(user_id).await.unwrap().expect("B not in room");
2118 member.membership().clone()
2119 }
2120
2121 fn direct_targets(client: &BaseClient, room_id: &RoomId) -> HashSet<OwnedDirectUserIdentifier> {
2122 let room = client.get_room(room_id).expect("Room not found!");
2123 room.direct_targets()
2124 }
2125
2126 async fn create_dm(
2129 client: &BaseClient,
2130 room_id: &RoomId,
2131 my_id: &UserId,
2132 their_id: &UserId,
2133 other_state: MembershipState,
2134 ) {
2135 let mut room = http::response::Room::new();
2136 set_room_joined(&mut room, my_id);
2137
2138 match other_state {
2139 MembershipState::Join => {
2140 room.joined_count = Some(uint!(2));
2141 room.invited_count = None;
2142 }
2143
2144 MembershipState::Invite => {
2145 room.joined_count = Some(uint!(1));
2146 room.invited_count = Some(uint!(1));
2147 }
2148
2149 _ => {
2150 room.joined_count = Some(uint!(1));
2151 room.invited_count = None;
2152 }
2153 }
2154
2155 room.required_state.push(make_membership_event(their_id, other_state));
2156
2157 let mut response = response_with_room(room_id, room);
2158 set_direct_with(&mut response, their_id.to_owned(), vec![room_id.to_owned()]);
2159 client
2160 .process_sliding_sync(&response, &RequestedRequiredStates::default())
2161 .await
2162 .expect("Failed to process sync");
2163 }
2164
2165 async fn update_room_membership(
2167 client: &BaseClient,
2168 room_id: &RoomId,
2169 user_id: &UserId,
2170 new_state: MembershipState,
2171 ) {
2172 let mut room = http::response::Room::new();
2173 room.required_state.push(make_membership_event(user_id, new_state));
2174 let response = response_with_room(room_id, room);
2175 client
2176 .process_sliding_sync(&response, &RequestedRequiredStates::default())
2177 .await
2178 .expect("Failed to process sync");
2179 }
2180
2181 fn set_direct_with(
2182 response: &mut http::Response,
2183 user_id: OwnedUserId,
2184 room_ids: Vec<OwnedRoomId>,
2185 ) {
2186 let mut direct_content: BTreeMap<OwnedDirectUserIdentifier, Vec<OwnedRoomId>> =
2187 BTreeMap::new();
2188 direct_content.insert(user_id.into(), room_ids);
2189 response
2190 .extensions
2191 .account_data
2192 .global
2193 .push(make_global_account_data_event(DirectEventContent(direct_content)));
2194 }
2195
2196 fn response_with_room(room_id: &RoomId, room: http::response::Room) -> http::Response {
2197 let mut response = http::Response::new("5".to_owned());
2198 response.rooms.insert(room_id.to_owned(), room);
2199 response
2200 }
2201
2202 fn room_with_avatar(avatar_uri: &MxcUri, user_id: &UserId) -> http::response::Room {
2203 let mut room = http::response::Room::new();
2204
2205 let mut avatar_event_content = RoomAvatarEventContent::new();
2206 avatar_event_content.url = Some(avatar_uri.to_owned());
2207
2208 room.required_state.push(make_state_event(user_id, "", avatar_event_content, None));
2209
2210 room
2211 }
2212
2213 fn room_with_canonical_alias(
2214 room_alias_id: &RoomAliasId,
2215 user_id: &UserId,
2216 ) -> http::response::Room {
2217 let mut room = http::response::Room::new();
2218
2219 let mut canonical_alias_event_content = RoomCanonicalAliasEventContent::new();
2220 canonical_alias_event_content.alias = Some(room_alias_id.to_owned());
2221
2222 room.required_state.push(make_state_event(
2223 user_id,
2224 "",
2225 canonical_alias_event_content,
2226 None,
2227 ));
2228
2229 room
2230 }
2231
2232 fn room_with_name(name: &str, user_id: &UserId) -> http::response::Room {
2233 let mut room = http::response::Room::new();
2234
2235 let name_event_content = RoomNameEventContent::new(name.to_owned());
2236
2237 room.required_state.push(make_state_event(user_id, "", name_event_content, None));
2238
2239 room
2240 }
2241
2242 fn set_room_name(room: &mut http::response::Room, sender: &UserId, name: String) {
2243 room.required_state.push(make_state_event(
2244 sender,
2245 "",
2246 RoomNameEventContent::new(name),
2247 None,
2248 ));
2249 }
2250
2251 fn set_room_invited(room: &mut http::response::Room, inviter: &UserId, invitee: &UserId) {
2252 let evt = Raw::new(&json!({
2256 "type": "m.room.member",
2257 "sender": inviter,
2258 "content": {
2259 "is_direct": true,
2260 "membership": "invite",
2261 },
2262 "state_key": invitee,
2263 }))
2264 .expect("Failed to make raw event")
2265 .cast_unchecked();
2266
2267 room.invite_state = Some(vec![evt]);
2268
2269 room.required_state.push(make_state_event(
2272 inviter,
2273 invitee.as_str(),
2274 RoomMemberEventContent::new(MembershipState::Invite),
2275 None,
2276 ));
2277 }
2278
2279 fn set_room_knocked(room: &mut http::response::Room, knocker: &UserId) {
2280 let evt = Raw::new(&json!({
2284 "type": "m.room.member",
2285 "sender": knocker,
2286 "content": {
2287 "is_direct": true,
2288 "membership": "knock",
2289 },
2290 "state_key": knocker,
2291 }))
2292 .expect("Failed to make raw event")
2293 .cast_unchecked();
2294
2295 room.invite_state = Some(vec![evt]);
2296 }
2297
2298 fn set_room_joined(room: &mut http::response::Room, user_id: &UserId) {
2299 room.required_state.push(make_membership_event(user_id, MembershipState::Join));
2300 }
2301
2302 fn set_room_left(room: &mut http::response::Room, user_id: &UserId) {
2303 room.required_state.push(make_membership_event(user_id, MembershipState::Leave));
2304 }
2305
2306 fn set_room_left_as_timeline_event(room: &mut http::response::Room, user_id: &UserId) {
2307 room.timeline.push(make_membership_event(user_id, MembershipState::Leave));
2308 }
2309
2310 fn set_room_is_encrypted(room: &mut http::response::Room, user_id: &UserId) {
2311 room.required_state.push(make_encryption_event(user_id));
2312 }
2313
2314 fn make_membership_event<K>(user_id: &UserId, state: MembershipState) -> Raw<K> {
2315 make_state_event(user_id, user_id.as_str(), RoomMemberEventContent::new(state), None)
2316 }
2317
2318 fn make_encryption_event<K>(user_id: &UserId) -> Raw<K> {
2319 make_state_event(user_id, "", RoomEncryptionEventContent::with_recommended_defaults(), None)
2320 }
2321
2322 fn make_global_account_data_event<C: GlobalAccountDataEventContent, E>(content: C) -> Raw<E> {
2323 Raw::new(&json!({
2324 "type": content.event_type(),
2325 "content": content,
2326 }))
2327 .expect("Failed to create account data event")
2328 .cast_unchecked()
2329 }
2330
2331 fn make_state_event<C: StateEventContent, E>(
2332 sender: &UserId,
2333 state_key: &str,
2334 content: C,
2335 prev_content: Option<C>,
2336 ) -> Raw<E> {
2337 let unsigned = if let Some(prev_content) = prev_content {
2338 json!({ "prev_content": prev_content })
2339 } else {
2340 json!({})
2341 };
2342
2343 Raw::new(&json!({
2344 "type": content.event_type(),
2345 "state_key": state_key,
2346 "content": content,
2347 "event_id": event_id!("$evt"),
2348 "sender": sender,
2349 "origin_server_ts": 10,
2350 "unsigned": unsigned,
2351 }))
2352 .expect("Failed to create state event")
2353 .cast_unchecked()
2354 }
2355}