@@ -8,7 +8,7 @@ use matrix_sdk::{
88 config:: SyncSettings ,
99 test_utils:: {
1010 logged_in_client_with_server,
11- mocks:: { MatrixMockServer , RoomMessagesResponseTemplate } ,
11+ mocks:: { MatrixMockServer , RoomMessagesResponseTemplate , RoomRelationsResponseTemplate } ,
1212 } ,
1313} ;
1414use matrix_sdk_base:: deserialized_responses:: TimelineEvent ;
@@ -22,7 +22,7 @@ use ruma::{
2222 EventId , MilliSecondsSinceUnixEpoch , OwnedEventId , OwnedRoomId , RoomId , UserId , assign,
2323 event_id,
2424 events:: {
25- AnySyncTimelineEvent ,
25+ AnySyncTimelineEvent , AnyTimelineEvent ,
2626 room:: {
2727 encrypted:: {
2828 EncryptedEventScheme , MegolmV1AesSha2ContentInit , RoomEncryptedEventContent ,
@@ -52,15 +52,25 @@ async fn test_new_pinned_events_are_not_added_on_sync() {
5252 let room_id = room_id ! ( "!test:localhost" ) ;
5353
5454 let f = EventFactory :: new ( ) . room ( room_id) . sender ( * BOB ) ;
55+ let event_id = event_id ! ( "$1" ) ;
5556 let event_1 = f
5657 . text_msg ( "in the end" )
57- . event_id ( event_id ! ( "$1" ) )
58+ . event_id ( event_id)
5859 . server_ts ( MilliSecondsSinceUnixEpoch :: now ( ) )
5960 . into_raw_sync ( ) ;
6061
61- // Mock /event endpoint for a timeline event
62+ // Mock /event endpoint for event $1.
6263 mock_events_endpoint ( & server, room_id, vec ! [ event_1] ) . await ;
6364
65+ // Mock /relations for event $1.
66+ server
67+ . mock_room_relations ( )
68+ . match_target_event ( event_id. to_owned ( ) )
69+ . ok ( RoomRelationsResponseTemplate :: default ( ) . events ( Vec :: < Raw < AnyTimelineEvent > > :: new ( ) ) )
70+ . mock_once ( )
71+ . mount ( )
72+ . await ;
73+
6474 // Load initial timeline items: a `m.room.pinned_events` with events $1 and $2
6575 // pinned
6676 let room = PinnedEventsSync :: new ( room_id)
@@ -103,27 +113,178 @@ async fn test_new_pinned_events_are_not_added_on_sync() {
103113 assert_pending ! ( timeline_stream) ;
104114}
105115
116+ #[ async_test]
117+ async fn test_pinned_event_with_reaction ( ) {
118+ let server = MatrixMockServer :: new ( ) . await ;
119+ let client = server. client_builder ( ) . build ( ) . await ;
120+ let room_id = room_id ! ( "!test:localhost" ) ;
121+
122+ let f = EventFactory :: new ( ) . room ( room_id) . sender ( * BOB ) ;
123+ let event_id = event_id ! ( "$1" ) ;
124+ let event_1 = f
125+ . text_msg ( "in the end" )
126+ . event_id ( event_id)
127+ . server_ts ( MilliSecondsSinceUnixEpoch :: now ( ) )
128+ . into_raw_sync ( ) ;
129+
130+ let reaction = f. reaction ( event_id, "👀" ) . into_raw_timeline ( ) ;
131+
132+ // Mock /event endpoint for event $1.
133+ mock_events_endpoint ( & server, room_id, vec ! [ event_1] ) . await ;
134+
135+ // Mock /relations for event $1.
136+ server
137+ . mock_room_relations ( )
138+ . match_target_event ( event_id. to_owned ( ) )
139+ . ok ( RoomRelationsResponseTemplate :: default ( ) . events ( vec ! [ reaction] ) )
140+ . mock_once ( )
141+ . mount ( )
142+ . await ;
143+
144+ // Load initial timeline items: a `m.room.pinned_events` with event $1 pinned
145+ let room = PinnedEventsSync :: new ( room_id)
146+ . with_pinned_event_ids ( vec ! [ "$1" ] )
147+ . mock_and_sync ( & client, & server)
148+ . await
149+ . expect ( "Room should be synced" ) ;
150+ let timeline =
151+ TimelineBuilder :: new ( & room) . with_focus ( pinned_events_focus ( 100 ) ) . build ( ) . await . unwrap ( ) ;
152+
153+ assert ! (
154+ timeline. live_back_pagination_status( ) . await . is_none( ) ,
155+ "there should be no live back-pagination status for a pinned events timeline"
156+ ) ;
157+
158+ // Load timeline items
159+ let ( items, mut timeline_stream) = timeline. subscribe ( ) . await ;
160+
161+ // Verify that the timeline contains the pinned event and its reaction.
162+ assert_eq ! ( items. len( ) , 1 + 1 ) ; // event item + a date divider
163+ assert ! ( items[ 0 ] . is_date_divider( ) ) ;
164+ assert_eq ! ( items[ 1 ] . as_event( ) . unwrap( ) . content( ) . as_message( ) . unwrap( ) . body( ) , "in the end" ) ;
165+ let reactions = items[ 1 ]
166+ . as_event ( )
167+ . unwrap ( )
168+ . content ( )
169+ . reactions ( )
170+ . expect ( "pinned event should have reactions" ) ;
171+ assert_eq ! ( reactions. len( ) , 1 ) ;
172+ assert ! ( reactions. get( "👀" ) . is_some( ) ) ;
173+ assert_pending ! ( timeline_stream) ;
174+ }
175+
176+ #[ async_test]
177+ async fn test_pinned_event_with_paginated_reactions ( ) {
178+ let server = MatrixMockServer :: new ( ) . await ;
179+ let client = server. client_builder ( ) . build ( ) . await ;
180+ let room_id = room_id ! ( "!test:localhost" ) ;
181+
182+ let f = EventFactory :: new ( ) . room ( room_id) . sender ( * BOB ) ;
183+ let event_id = event_id ! ( "$1" ) ;
184+ let event_1 = f
185+ . text_msg ( "in the end" )
186+ . event_id ( event_id)
187+ . server_ts ( MilliSecondsSinceUnixEpoch :: now ( ) )
188+ . into_raw_sync ( ) ;
189+
190+ let reaction_1 = f. reaction ( event_id, "👀" ) . into_raw_timeline ( ) ;
191+ let reaction_2 = f. reaction ( event_id, "🤔" ) . into_raw_timeline ( ) ;
192+
193+ // Mock /event endpoint for event $1.
194+ mock_events_endpoint ( & server, room_id, vec ! [ event_1] ) . await ;
195+
196+ // Mock /relations for event $1, split across two pages.
197+ let token = "foo" ;
198+ server
199+ . mock_room_relations ( )
200+ . match_target_event ( event_id. to_owned ( ) )
201+ . ok ( RoomRelationsResponseTemplate :: default ( ) . events ( vec ! [ reaction_1] ) . next_batch ( token) )
202+ . mock_once ( )
203+ . mount ( )
204+ . await ;
205+ server
206+ . mock_room_relations ( )
207+ . match_target_event ( event_id. to_owned ( ) )
208+ . match_from ( token)
209+ . ok ( RoomRelationsResponseTemplate :: default ( ) . events ( vec ! [ reaction_2] ) )
210+ . mock_once ( )
211+ . mount ( )
212+ . await ;
213+
214+ // Load initial timeline items: a `m.room.pinned_events` with event $1 pinned
215+ let room = PinnedEventsSync :: new ( room_id)
216+ . with_pinned_event_ids ( vec ! [ "$1" ] )
217+ . mock_and_sync ( & client, & server)
218+ . await
219+ . expect ( "Room should be synced" ) ;
220+ let timeline =
221+ TimelineBuilder :: new ( & room) . with_focus ( pinned_events_focus ( 100 ) ) . build ( ) . await . unwrap ( ) ;
222+
223+ assert ! (
224+ timeline. live_back_pagination_status( ) . await . is_none( ) ,
225+ "there should be no live back-pagination status for a pinned events timeline"
226+ ) ;
227+
228+ // Load timeline items
229+ let ( items, mut timeline_stream) = timeline. subscribe ( ) . await ;
230+
231+ // Verify that the timeline contains the pinned event and its reactions.
232+ assert_eq ! ( items. len( ) , 1 + 1 ) ; // event item + a date divider
233+ assert ! ( items[ 0 ] . is_date_divider( ) ) ;
234+ assert_eq ! ( items[ 1 ] . as_event( ) . unwrap( ) . content( ) . as_message( ) . unwrap( ) . body( ) , "in the end" ) ;
235+ let reactions = items[ 1 ]
236+ . as_event ( )
237+ . unwrap ( )
238+ . content ( )
239+ . reactions ( )
240+ . expect ( "pinned event should have reactions" ) ;
241+ assert_eq ! ( reactions. len( ) , 2 ) ;
242+ assert ! ( reactions. get( "👀" ) . is_some( ) ) ;
243+ assert ! ( reactions. get( "🤔" ) . is_some( ) ) ;
244+ assert_pending ! ( timeline_stream) ;
245+ }
246+
106247#[ async_test]
107248async fn test_new_pinned_event_ids_reload_the_timeline ( ) {
108249 let server = MatrixMockServer :: new ( ) . await ;
109250 let client = server. client_builder ( ) . build ( ) . await ;
110251 let room_id = room_id ! ( "!test:localhost" ) ;
111252
112253 let f = EventFactory :: new ( ) . room ( room_id) . sender ( * BOB ) ;
254+ let event_id_1 = event_id ! ( "$1" ) ;
113255 let event_1 = f
114256 . text_msg ( "in the end" )
115- . event_id ( event_id ! ( "$1" ) )
257+ . event_id ( event_id_1 )
116258 . server_ts ( MilliSecondsSinceUnixEpoch :: now ( ) )
117259 . into_raw_sync ( ) ;
260+ let event_id_2 = event_id ! ( "$2" ) ;
118261 let event_2 = f
119262 . text_msg ( "it doesn't even matter" )
120- . event_id ( event_id ! ( "$2" ) )
263+ . event_id ( event_id_2 )
121264 . server_ts ( MilliSecondsSinceUnixEpoch :: now ( ) )
122265 . into_raw_sync ( ) ;
123266
267+ // Mock /event endpoint for events $1 and $2.
268+ mock_events_endpoint ( & server, room_id, vec ! [ event_1. clone( ) , event_2. clone( ) ] ) . await ;
269+
270+ // Mock /relations endpoint for events $1 and $2.
271+ server
272+ . mock_room_relations ( )
273+ . match_target_event ( event_id_1. to_owned ( ) )
274+ . ok ( RoomRelationsResponseTemplate :: default ( ) . events ( Vec :: < Raw < AnyTimelineEvent > > :: new ( ) ) )
275+ . mock_once ( )
276+ . mount ( )
277+ . await ;
278+ server
279+ . mock_room_relations ( )
280+ . match_target_event ( event_id_2. to_owned ( ) )
281+ . ok ( RoomRelationsResponseTemplate :: default ( ) . events ( Vec :: < Raw < AnyTimelineEvent > > :: new ( ) ) )
282+ . mock_once ( )
283+ . mount ( )
284+ . await ;
285+
124286 // Load initial timeline items: 2 text messages and a `m.room.pinned_events`
125287 // with event $1 and $2 pinned
126- mock_events_endpoint ( & server, room_id, vec ! [ event_1. clone( ) , event_2. clone( ) ] ) . await ;
127288 let room = PinnedEventsSync :: new ( room_id)
128289 . with_timeline_events ( vec ! [ event_2. clone( ) ] )
129290 . with_pinned_event_ids ( vec ! [ "$1" ] )
@@ -225,15 +386,25 @@ async fn test_cached_events_are_kept_for_different_room_instances() {
225386 event_cache. subscribe ( ) . unwrap ( ) ;
226387
227388 let f = EventFactory :: new ( ) . room ( room_id) . sender ( * BOB ) ;
389+ let event_id = event_id ! ( "$1" ) ;
228390 let pinned_event = f
229391 . text_msg ( "in the end" )
230- . event_id ( event_id ! ( "$1" ) )
392+ . event_id ( event_id)
231393 . server_ts ( MilliSecondsSinceUnixEpoch :: now ( ) )
232394 . into_raw_sync ( ) ;
233395
234- // Mock /event for some timeline events
396+ // Mock /event for the pinned event.
235397 mock_events_endpoint ( & server, room_id, vec ! [ pinned_event] ) . await ;
236398
399+ // Mock /relations for the pinned event.
400+ server
401+ . mock_room_relations ( )
402+ . match_target_event ( event_id. to_owned ( ) )
403+ . ok ( RoomRelationsResponseTemplate :: default ( ) . events ( Vec :: < Raw < AnyTimelineEvent > > :: new ( ) ) )
404+ . mock_once ( )
405+ . mount ( )
406+ . await ;
407+
237408 // Load initial timeline items: a `m.room.pinned_events` with event $1 and $2
238409 // pinned
239410 let room = PinnedEventsSync :: new ( room_id)
@@ -427,6 +598,13 @@ async fn test_pinned_timeline_with_pinned_utd_on_sync_contains_it() {
427598 // Mock encrypted pinned event for which we don't have keys (an UTD)
428599 let utd_event = create_utd ( room_id, & sender_id, event_id) ;
429600 mock_events_endpoint ( & server, room_id, vec ! [ utd_event] ) . await ;
601+ server
602+ . mock_room_relations ( )
603+ . match_target_event ( event_id. to_owned ( ) )
604+ . ok ( RoomRelationsResponseTemplate :: default ( ) . events ( Vec :: < Raw < AnyTimelineEvent > > :: new ( ) ) )
605+ . mock_once ( )
606+ . mount ( )
607+ . await ;
430608
431609 let timeline =
432610 TimelineBuilder :: new ( & room) . with_focus ( pinned_events_focus ( 1 ) ) . build ( ) . await . unwrap ( ) ;
@@ -445,15 +623,25 @@ async fn test_edited_events_are_reflected_in_sync() {
445623 let room_id = room_id ! ( "!test:localhost" ) ;
446624
447625 let f = EventFactory :: new ( ) . room ( room_id) . sender ( * BOB ) ;
626+ let event_id = event_id ! ( "$1" ) ;
448627 let pinned_event = f
449628 . text_msg ( "in the end" )
450- . event_id ( event_id ! ( "$1" ) )
629+ . event_id ( event_id)
451630 . server_ts ( MilliSecondsSinceUnixEpoch :: now ( ) )
452631 . into_raw_sync ( ) ;
453632
454- // Mock /event for some timeline events .
633+ // Mock /event for the pinned event .
455634 mock_events_endpoint ( & server, room_id, vec ! [ pinned_event] ) . await ;
456635
636+ // Mock /relations for the pinned event.
637+ server
638+ . mock_room_relations ( )
639+ . match_target_event ( event_id. to_owned ( ) )
640+ . ok ( RoomRelationsResponseTemplate :: default ( ) . events ( Vec :: < Raw < AnyTimelineEvent > > :: new ( ) ) )
641+ . mock_once ( )
642+ . mount ( )
643+ . await ;
644+
457645 // Load initial timeline items: a text message and a `m.room.pinned_events` with
458646 // event $1.
459647 let room = PinnedEventsSync :: new ( room_id)
@@ -517,15 +705,25 @@ async fn test_redacted_events_are_reflected_in_sync() {
517705 let room_id = room_id ! ( "!test:localhost" ) ;
518706
519707 let f = EventFactory :: new ( ) . room ( room_id) . sender ( * BOB ) ;
708+ let event_id = event_id ! ( "$1" ) ;
520709 let pinned_event = f
521710 . text_msg ( "in the end" )
522711 . event_id ( event_id ! ( "$1" ) )
523712 . server_ts ( MilliSecondsSinceUnixEpoch :: now ( ) )
524713 . into_raw_sync ( ) ;
525714
526- // Mock /event for some timeline events
715+ // Mock /event for the pinned timeline event.
527716 mock_events_endpoint ( & server, room_id, vec ! [ pinned_event] ) . await ;
528717
718+ // Mock /relations for pinned timeline event.
719+ server
720+ . mock_room_relations ( )
721+ . match_target_event ( event_id. to_owned ( ) )
722+ . ok ( RoomRelationsResponseTemplate :: default ( ) . events ( Vec :: < Raw < AnyTimelineEvent > > :: new ( ) ) )
723+ . mock_once ( )
724+ . mount ( )
725+ . await ;
726+
529727 // Load initial timeline items: a text message and a `m.room.pinned_events` with
530728 // event $1
531729 let room = PinnedEventsSync :: new ( room_id)
0 commit comments