1mod builder;
16
17use std::ops::{Deref, DerefMut, Not};
18
19pub use builder::filter_timeline_event;
20use builder::{BufferOfValuesForLocalEvents, Builder};
21use eyeball::{AsyncLock, ObservableWriteGuard, SharedObservable, Subscriber};
22pub use matrix_sdk_base::latest_event::{
23 LatestEventValue, LocalLatestEventValue, RemoteLatestEventValue,
24};
25use matrix_sdk_base::{RoomInfoNotableUpdateReasons, RoomState};
26use ruma::{EventId, OwnedEventId, UserId, events::room::power_levels::RoomPowerLevels};
27use tracing::{error, info, instrument, trace, warn};
28
29use crate::{Room, event_cache::RoomEventCache, room::WeakRoom, send_queue::RoomSendQueueUpdate};
30
31#[derive(Clone, Copy, Debug)]
34pub(super) enum NeedMoreEvents {
35 Yes,
36 No,
37}
38
39#[derive(Debug)]
43pub(super) struct LatestEvent {
44 weak_room: WeakRoom,
46
47 _thread_id: Option<OwnedEventId>,
49
50 buffer_of_values_for_local_events: BufferOfValuesForLocalEvents,
54
55 current_value: SharedObservable<LatestEventValue, AsyncLock>,
57}
58
59impl LatestEvent {
60 pub fn new(
61 weak_room: &WeakRoom,
62 thread_id: Option<&EventId>,
63 ) -> With<Self, IsLatestEventValueNone> {
64 let latest_event_value = match thread_id {
65 Some(_thread_id) => LatestEventValue::default(),
66 None => weak_room.get().map(|room| room.latest_event()).unwrap_or_default(),
67 };
68 let is_none = latest_event_value.is_none();
69
70 With {
71 result: Self {
72 weak_room: weak_room.clone(),
73 _thread_id: thread_id.map(ToOwned::to_owned),
74 buffer_of_values_for_local_events: BufferOfValuesForLocalEvents::new(),
75 current_value: SharedObservable::new_async(latest_event_value),
76 },
77 with: is_none,
78 }
79 }
80
81 pub async fn subscribe(&self) -> Subscriber<LatestEventValue, AsyncLock> {
83 self.current_value.subscribe().await
84 }
85
86 #[cfg(test)]
87 pub async fn get(&self) -> LatestEventValue {
88 self.current_value.get().await
89 }
90
91 pub async fn update_with_event_cache(
104 &mut self,
105 room_event_cache: &RoomEventCache,
106 own_user_id: &UserId,
107 power_levels: Option<&RoomPowerLevels>,
108 ) -> NeedMoreEvents {
109 if self.buffer_of_values_for_local_events.is_empty().not() {
110 return NeedMoreEvents::No;
114 }
115
116 let current_event = self.current_value.get().await;
117 let new_value =
118 Builder::new_remote(room_event_cache, current_event, own_user_id, power_levels).await;
119
120 trace!(value = ?new_value, "Computed a remote `LatestEventValue`");
121
122 let need_more_events = match new_value {
123 Some(LatestEventValue::Remote(_)) => NeedMoreEvents::No,
124 _ => NeedMoreEvents::Yes,
125 };
126
127 if let Some(new_value) = new_value {
128 self.update(new_value).await;
129 }
130
131 need_more_events
132 }
133
134 pub async fn update_with_send_queue(
137 &mut self,
138 send_queue_update: &RoomSendQueueUpdate,
139 room_event_cache: &RoomEventCache,
140 own_user_id: &UserId,
141 power_levels: Option<&RoomPowerLevels>,
142 ) {
143 let current_event = self.current_value.get().await;
144 let new_value = Builder::new_local(
145 send_queue_update,
146 &mut self.buffer_of_values_for_local_events,
147 room_event_cache,
148 current_event,
149 own_user_id,
150 power_levels,
151 )
152 .await;
153
154 trace!(value = ?new_value, "Computed a local `LatestEventValue`");
155
156 if let Some(new_value) = new_value {
157 self.update(new_value).await;
158 }
159 }
160
161 pub async fn update_with_room_info(
163 &mut self,
164 room: Room,
165 reasons: RoomInfoNotableUpdateReasons,
166 ) {
167 if reasons.contains(RoomInfoNotableUpdateReasons::MEMBERSHIP) {
169 let new_value = match room.state() {
170 RoomState::Invited => {
173 if matches!(
185 self.current_value.read().await.deref(),
186 LatestEventValue::RemoteInvite { .. }
187 ) {
188 return;
189 }
190
191 let new_value = Builder::new_remote_for_invite(&room).await;
192
193 trace!(value = ?new_value, "Computed a remote `LatestEventValue` for invite");
194
195 new_value
196 }
197
198 _ => {
199 info!(
200 "Skipping the computation of a remote `LatestEventValue` from a `RoomInfo`"
201 );
202
203 return;
204 }
205 };
206
207 self.update(new_value).await;
208 }
209 }
210
211 async fn update(&mut self, new_value: LatestEventValue) {
219 {
230 let mut guard = self.current_value.write().await;
231 let previous_value = guard.deref();
232
233 let do_update = match (previous_value, &new_value) {
234 (LatestEventValue::None, LatestEventValue::None) => false,
236
237 (_, LatestEventValue::None) | (LatestEventValue::None, _) => true,
239
240 (
243 _,
244 LatestEventValue::LocalIsSending(_) | LatestEventValue::LocalCannotBeSent(_),
245 ) => true,
246
247 (previous, new) => match (previous.event_id(), new.event_id()) {
250 (Some(previous_event_id), Some(new_event_id)) => {
251 previous_event_id != new_event_id
252 }
253 _ => true,
254 },
255 };
256
257 if do_update {
258 ObservableWriteGuard::set(&mut guard, new_value.clone());
259
260 drop(guard);
262
263 self.store(new_value).await;
264 }
265 }
266 }
267
268 #[instrument(skip_all)]
273 async fn store(&mut self, new_value: LatestEventValue) {
274 let Some(room) = self.weak_room.get() else {
275 warn!(room_id = ?self.weak_room.room_id(), "Cannot store the latest event value because the room cannot be accessed");
276 return;
277 };
278 let result = room
279 .update_and_save_room_info(|mut info| {
280 info.set_latest_event(new_value);
281 (info, RoomInfoNotableUpdateReasons::LATEST_EVENT)
282 })
283 .await;
284 if let Err(error) = result {
285 error!(room_id = ?room.room_id(), ?error, "Failed to save the changes");
286 }
287 }
288}
289
290pub(super) struct With<T, W> {
293 result: T,
295
296 with: W,
298}
299
300impl<T, W> With<T, W> {
301 pub fn map<F, O>(this: With<T, W>, f: F) -> With<O, W>
303 where
304 F: FnOnce(T) -> O,
305 {
306 With { result: f(this.result), with: this.with }
307 }
308
309 pub fn inner(this: With<T, W>) -> T {
311 this.result
312 }
313
314 pub fn unzip(this: With<T, W>) -> (T, W) {
316 (this.result, this.with)
317 }
318}
319
320impl<T, W> Deref for With<T, W> {
321 type Target = T;
322
323 fn deref(&self) -> &Self::Target {
324 &self.result
325 }
326}
327
328impl<T, W> DerefMut for With<T, W> {
329 fn deref_mut(&mut self) -> &mut Self::Target {
330 &mut self.result
331 }
332}
333
334pub(super) type IsLatestEventValueNone = bool;
335
336#[cfg(all(not(target_family = "wasm"), test))]
337mod tests_latest_event {
338 use std::ops::Not;
339
340 use assert_matches::assert_matches;
341 use matrix_sdk_base::{
342 RoomInfoNotableUpdateReasons, RoomState,
343 latest_event::RemoteLatestEventValue,
344 linked_chunk::{ChunkIdentifier, LinkedChunkId, Position, Update},
345 store::{SerializableEventContent, StoreConfig},
346 };
347 use matrix_sdk_common::cross_process_lock::CrossProcessLockConfig;
348 use matrix_sdk_test::{JoinedRoomBuilder, async_test, event_factory::EventFactory};
349 use ruma::{
350 MilliSecondsSinceUnixEpoch, OwnedTransactionId, event_id,
351 events::{AnyMessageLikeEventContent, room::message::RoomMessageEventContent},
352 owned_event_id, owned_room_id, owned_user_id, room_id, user_id,
353 };
354 use stream_assert::{assert_next_matches, assert_pending};
355 use tokio::task::yield_now;
356
357 use super::{super::local_room_message, LatestEvent, LatestEventValue, With};
358 use crate::{
359 client::WeakClient,
360 room::WeakRoom,
361 send_queue::{LocalEcho, LocalEchoContent, RoomSendQueue, RoomSendQueueUpdate, SendHandle},
362 test_utils::mocks::MatrixMockServer,
363 };
364
365 fn new_local_echo_content(
366 room_send_queue: &RoomSendQueue,
367 transaction_id: &OwnedTransactionId,
368 body: &str,
369 ) -> LocalEchoContent {
370 LocalEchoContent::Event {
371 serialized_event: SerializableEventContent::new(
372 &AnyMessageLikeEventContent::RoomMessage(RoomMessageEventContent::text_plain(body)),
373 )
374 .unwrap(),
375 send_handle: SendHandle::new(
376 room_send_queue.clone(),
377 transaction_id.clone(),
378 MilliSecondsSinceUnixEpoch::now(),
379 ),
380 send_error: None,
381 }
382 }
383
384 #[async_test]
385 async fn test_new_loads_from_room_info() {
386 let room_id = room_id!("!r0");
387
388 let server = MatrixMockServer::new().await;
389 let client = server.client_builder().build().await;
390 let weak_client = WeakClient::from_client(&client);
391
392 let room = client.base_client().get_or_create_room(room_id, RoomState::Joined);
394 let weak_room = WeakRoom::new(weak_client, room_id.to_owned());
395
396 {
398 let (latest_event, is_none) = With::unzip(LatestEvent::new(&weak_room, None));
399
400 assert_matches!(latest_event.current_value.get().await, LatestEventValue::None);
402 assert!(is_none);
403 }
404
405 {
407 room.update_room_info(|mut info| {
408 info.set_latest_event(LatestEventValue::LocalIsSending(local_room_message("foo")));
409 (info, Default::default())
410 })
411 .await;
412 }
413
414 {
416 let (latest_event, is_none) = With::unzip(LatestEvent::new(&weak_room, None));
417
418 assert_matches!(
420 latest_event.current_value.get().await,
421 LatestEventValue::LocalIsSending(_)
422 );
423 assert!(is_none.not());
424 }
425 }
426
427 #[async_test]
428 async fn test_update_do_not_ignore_none_value() {
429 let room_id = room_id!("!r0");
430
431 let server = MatrixMockServer::new().await;
432 let client = server.client_builder().build().await;
433 let weak_client = WeakClient::from_client(&client);
434
435 client.base_client().get_or_create_room(room_id, RoomState::Joined);
437 let weak_room = WeakRoom::new(weak_client, room_id.to_owned());
438
439 let event_cache = client.event_cache();
441 event_cache.subscribe().unwrap();
442
443 let mut latest_event = LatestEvent::new(&weak_room, None);
444
445 assert_matches!(latest_event.current_value.get().await, LatestEventValue::None);
447
448 latest_event.update(LatestEventValue::LocalIsSending(local_room_message("foo"))).await;
450
451 assert_matches!(
452 latest_event.current_value.get().await,
453 LatestEventValue::LocalIsSending(_)
454 );
455
456 latest_event.update(LatestEventValue::None).await;
458
459 assert_matches!(latest_event.current_value.get().await, LatestEventValue::None);
460 }
461
462 #[async_test]
463 async fn test_update_ignore_none_if_previous_value_is_none() {
464 let room_id = room_id!("!r0");
465
466 let server = MatrixMockServer::new().await;
467 let client = server.client_builder().build().await;
468 let weak_client = WeakClient::from_client(&client);
469
470 client.base_client().get_or_create_room(room_id, RoomState::Joined);
472 let weak_room = WeakRoom::new(weak_client, room_id.to_owned());
473
474 let mut latest_event = LatestEvent::new(&weak_room, None);
475
476 let mut stream = latest_event.subscribe().await;
477 assert_pending!(stream);
478
479 latest_event.update(LatestEventValue::LocalIsSending(local_room_message("foo"))).await;
481 assert_next_matches!(stream, LatestEventValue::LocalIsSending(_));
483
484 latest_event.update(LatestEventValue::None).await;
486 assert_next_matches!(stream, LatestEventValue::None);
488
489 latest_event.update(LatestEventValue::None).await;
491 assert_pending!(stream);
493
494 latest_event.update(LatestEventValue::None).await;
496 assert_pending!(stream);
498
499 latest_event.update(LatestEventValue::LocalIsSending(local_room_message("bar"))).await;
501 assert_next_matches!(stream, LatestEventValue::LocalIsSending(_));
503
504 assert_pending!(stream);
505 }
506
507 #[async_test]
508 async fn test_updates_do_not_ignore_local_values() {
509 let room_id = room_id!("!r0");
510
511 let server = MatrixMockServer::new().await;
512 let client = server.client_builder().build().await;
513 let weak_client = WeakClient::from_client(&client);
514
515 client.base_client().get_or_create_room(room_id, RoomState::Joined);
516 let weak_room = WeakRoom::new(weak_client, room_id.to_owned());
517
518 let mut latest_event = LatestEvent::new(&weak_room, None);
519
520 let mut stream = latest_event.subscribe().await;
521 assert_pending!(stream);
522
523 latest_event.update(LatestEventValue::LocalIsSending(local_room_message("foo"))).await;
525 assert_next_matches!(stream, LatestEventValue::LocalIsSending(_));
526
527 latest_event.update(LatestEventValue::LocalIsSending(local_room_message("bar"))).await;
529 assert_next_matches!(stream, LatestEventValue::LocalIsSending(_));
530
531 latest_event.update(LatestEventValue::LocalCannotBeSent(local_room_message("bar"))).await;
533 assert_next_matches!(stream, LatestEventValue::LocalCannotBeSent(_));
534
535 latest_event.update(LatestEventValue::LocalIsSending(local_room_message("bar"))).await;
537 assert_next_matches!(stream, LatestEventValue::LocalIsSending(_));
538
539 assert_pending!(stream);
540 }
541
542 #[async_test]
543 async fn test_update_does_not_ignore_a_new_value_that_has_no_event_id() {
544 let room_id = room_id!("!r0");
545
546 let server = MatrixMockServer::new().await;
547 let client = server.client_builder().build().await;
548 let weak_client = WeakClient::from_client(&client);
549
550 client.base_client().get_or_create_room(room_id, RoomState::Joined);
551 let weak_room = WeakRoom::new(weak_client, room_id.to_owned());
552
553 let mut latest_event = LatestEvent::new(&weak_room, None);
554
555 let mut stream = latest_event.subscribe().await;
556 assert_pending!(stream);
557
558 latest_event.update(LatestEventValue::LocalIsSending(local_room_message("foo"))).await;
560 assert_next_matches!(stream, LatestEventValue::LocalIsSending(_));
561
562 latest_event
565 .update(LatestEventValue::RemoteInvite {
566 event_id: None,
567 timestamp: MilliSecondsSinceUnixEpoch::now(),
568 inviter: Some(owned_user_id!("@mnt_io:matrix.org")),
569 })
570 .await;
571 assert_next_matches!(stream, LatestEventValue::RemoteInvite { .. });
572
573 assert_pending!(stream);
574 }
575
576 #[async_test]
577 async fn test_update_ignore_when_previous_value_has_the_same_event_id() {
578 let room_id = room_id!("!r0");
579 let user_id = user_id!("@mnt_io:matrix.org");
580 let event_factory = EventFactory::new().sender(user_id).room(room_id);
581
582 let server = MatrixMockServer::new().await;
583 let client = server.client_builder().build().await;
584 let weak_client = WeakClient::from_client(&client);
585
586 client.base_client().get_or_create_room(room_id, RoomState::Joined);
588 let weak_room = WeakRoom::new(weak_client, room_id.to_owned());
589
590 let mut latest_event = LatestEvent::new(&weak_room, None);
591
592 let mut stream = latest_event.subscribe().await;
593 assert_pending!(stream);
594
595 latest_event.update(LatestEventValue::LocalIsSending(local_room_message("foo"))).await;
597 assert_next_matches!(stream, LatestEventValue::LocalIsSending(_));
599
600 let first_event: RemoteLatestEventValue =
602 event_factory.text_msg("A").event_id(event_id!("$ev0")).into();
603 latest_event.update(LatestEventValue::Remote(first_event.clone())).await;
604 assert_next_matches!(stream, LatestEventValue::Remote(_));
606
607 latest_event.update(LatestEventValue::Remote(first_event)).await;
609 assert_pending!(stream);
611
612 let second_event = event_factory.text_msg("A").event_id(event_id!("$ev1")).into();
614 latest_event.update(LatestEventValue::Remote(second_event)).await;
615 assert_next_matches!(stream, LatestEventValue::Remote(_));
617
618 assert_pending!(stream);
619 }
620
621 #[async_test]
622 async fn test_local_has_priority_over_remote() {
623 let room_id = owned_room_id!("!r0");
624 let user_id = user_id!("@mnt_io:matrix.org");
625 let event_factory = EventFactory::new().sender(user_id).room(&room_id);
626
627 let server = MatrixMockServer::new().await;
628 let client = server.client_builder().build().await;
629 client.base_client().get_or_create_room(&room_id, RoomState::Joined);
630 let room = client.get_room(&room_id).unwrap();
631 let weak_room = WeakRoom::new(WeakClient::from_client(&client), room_id.clone());
632
633 let event_cache = client.event_cache();
634 event_cache.subscribe().unwrap();
635
636 client
638 .event_cache_store()
639 .lock()
640 .await
641 .expect("Could not acquire the event cache lock")
642 .as_clean()
643 .expect("Could not acquire a clean event cache lock")
644 .handle_linked_chunk_updates(
645 LinkedChunkId::Room(&room_id),
646 vec![
647 Update::NewItemsChunk {
648 previous: None,
649 new: ChunkIdentifier::new(0),
650 next: None,
651 },
652 Update::PushItems {
653 at: Position::new(ChunkIdentifier::new(0), 0),
654 items: vec![event_factory.text_msg("A").event_id(event_id!("$ev0")).into()],
655 },
656 ],
657 )
658 .await
659 .unwrap();
660
661 let (room_event_cache, _) = event_cache.room(&room_id).await.unwrap();
662
663 let send_queue = client.send_queue();
664 let room_send_queue = send_queue.for_room(room);
665
666 let mut latest_event = LatestEvent::new(&weak_room, None);
667
668 {
670 latest_event.update_with_event_cache(&room_event_cache, user_id, None).await;
671
672 assert_matches!(latest_event.current_value.get().await, LatestEventValue::Remote(_));
673 }
674
675 let transaction_id = OwnedTransactionId::from("txnid0");
678
679 {
680 let content = new_local_echo_content(&room_send_queue, &transaction_id, "B");
681
682 let update = RoomSendQueueUpdate::NewLocalEvent(LocalEcho {
683 transaction_id: transaction_id.clone(),
684 content,
685 });
686
687 latest_event.update_with_send_queue(&update, &room_event_cache, user_id, None).await;
688
689 assert_matches!(
690 latest_event.current_value.get().await,
691 LatestEventValue::LocalIsSending(_)
692 );
693 }
694
695 {
699 latest_event.update_with_event_cache(&room_event_cache, user_id, None).await;
700
701 assert_matches!(
702 latest_event.current_value.get().await,
703 LatestEventValue::LocalIsSending(_)
704 );
705 }
706
707 {
710 let update = RoomSendQueueUpdate::SentEvent {
711 transaction_id,
712 event_id: owned_event_id!("$ev1"),
713 };
714
715 latest_event.update_with_send_queue(&update, &room_event_cache, user_id, None).await;
716
717 assert_matches!(
718 latest_event.current_value.get().await,
719 LatestEventValue::LocalHasBeenSent { .. }
720 );
721 }
722
723 {
726 latest_event.update_with_event_cache(&room_event_cache, user_id, None).await;
727
728 assert_matches!(latest_event.current_value.get().await, LatestEventValue::Remote(_));
729 }
730 }
731
732 #[async_test]
733 async fn test_redacted_latest_event_is_removed() {
734 let room_id = owned_room_id!("!r0");
735 let user_id = user_id!("@mnt_io:matrix.org");
736 let event_factory = EventFactory::new().sender(user_id).room(&room_id);
737
738 let server = MatrixMockServer::new().await;
739 let client = server.client_builder().build().await;
740 client.base_client().get_or_create_room(&room_id, RoomState::Joined);
741 let _room = client.get_room(&room_id).unwrap();
742 let weak_room = WeakRoom::new(WeakClient::from_client(&client), room_id.clone());
743
744 let event_cache = client.event_cache();
745 event_cache.subscribe().unwrap();
746
747 let event_id_0 = event_id!("$ev0");
748 let event_id_1 = event_id!("$ev1");
749
750 client
752 .event_cache_store()
753 .lock()
754 .await
755 .expect("Could not acquire the event cache lock")
756 .as_clean()
757 .expect("Could not acquire a clean event cache lock")
758 .handle_linked_chunk_updates(
759 LinkedChunkId::Room(&room_id),
760 vec![
761 Update::NewItemsChunk {
762 previous: None,
763 new: ChunkIdentifier::new(0),
764 next: None,
765 },
766 Update::PushItems {
767 at: Position::new(ChunkIdentifier::new(0), 0),
768 items: vec![
769 event_factory.text_msg("A").event_id(event_id_0).into(),
770 event_factory.text_msg("B").event_id(event_id_1).into(),
771 ],
772 },
773 ],
774 )
775 .await
776 .unwrap();
777
778 let (room_event_cache, _) = event_cache.room(&room_id).await.unwrap();
779
780 let mut latest_event = LatestEvent::new(&weak_room, None);
781
782 {
784 latest_event.update_with_event_cache(&room_event_cache, user_id, None).await;
785
786 assert_matches!(
787 latest_event.current_value.get().await,
788 LatestEventValue::Remote(remote) => {
789 assert_eq!(remote.event_id(), Some(event_id_1));
790 }
791 );
792 }
793
794 {
796 server
797 .mock_sync()
798 .ok_and_run(&client, |builder| {
799 builder.add_joined_room(
800 JoinedRoomBuilder::new(&room_id)
801 .add_timeline_event(event_factory.redaction(event_id_1)),
802 );
803 })
804 .await;
805
806 yield_now().await;
807
808 latest_event.update_with_event_cache(&room_event_cache, user_id, None).await;
809
810 assert_matches!(
811 latest_event.current_value.get().await,
812 LatestEventValue::Remote(remote) => {
813 assert_eq!(remote.event_id(), Some(event_id_0));
815 }
816 );
817 }
818 }
819
820 #[async_test]
821 async fn test_store_latest_event_value() {
822 let room_id = owned_room_id!("!r0");
823 let user_id = user_id!("@mnt_io:matrix.org");
824 let event_factory = EventFactory::new().sender(user_id).room(&room_id);
825
826 let server = MatrixMockServer::new().await;
827
828 let store_config =
829 StoreConfig::new(CrossProcessLockConfig::multi_process("cross-process-lock-holder"));
830
831 {
833 let client = server
834 .client_builder()
835 .on_builder(|builder| builder.store_config(store_config.clone()))
836 .build()
837 .await;
838 let mut room_info_notable_update_receiver = client.room_info_notable_update_receiver();
839 let room = client.base_client().get_or_create_room(&room_id, RoomState::Joined);
840 let weak_room = WeakRoom::new(WeakClient::from_client(&client), room_id.clone());
841
842 let event_cache = client.event_cache();
843 event_cache.subscribe().unwrap();
844
845 client
847 .event_cache_store()
848 .lock()
849 .await
850 .expect("Could not acquire the event cache lock")
851 .as_clean()
852 .expect("Could not acquire a clean event cache lock")
853 .handle_linked_chunk_updates(
854 LinkedChunkId::Room(&room_id),
855 vec![
856 Update::NewItemsChunk {
857 previous: None,
858 new: ChunkIdentifier::new(0),
859 next: None,
860 },
861 Update::PushItems {
862 at: Position::new(ChunkIdentifier::new(0), 0),
863 items: vec![
864 event_factory.text_msg("A").event_id(event_id!("$ev0")).into(),
865 ],
866 },
867 ],
868 )
869 .await
870 .unwrap();
871
872 let (room_event_cache, _) = event_cache.room(&room_id).await.unwrap();
873
874 {
876 let latest_event = room.latest_event();
877
878 assert_matches!(latest_event, LatestEventValue::None);
879 }
880
881 {
883 let mut latest_event = LatestEvent::new(&weak_room, None);
884 latest_event.update_with_event_cache(&room_event_cache, user_id, None).await;
885
886 assert_matches!(
887 latest_event.current_value.get().await,
888 LatestEventValue::Remote(_)
889 );
890 }
891
892 {
894 let update = room_info_notable_update_receiver.recv().await.unwrap();
895
896 assert_eq!(update.room_id, room_id);
897 assert!(update.reasons.contains(RoomInfoNotableUpdateReasons::LATEST_EVENT));
898 }
899
900 {
902 let latest_event = room.latest_event();
903
904 assert_matches!(latest_event, LatestEventValue::Remote(_));
905 }
906 }
907
908 {
911 let client = server
912 .client_builder()
913 .on_builder(|builder| builder.store_config(store_config))
914 .build()
915 .await;
916 let room = client.get_room(&room_id).unwrap();
917 let latest_event = room.latest_event();
918
919 assert_matches!(latest_event, LatestEventValue::Remote(_));
920 }
921 }
922}