@@ -28,7 +28,8 @@ use matrix_sdk_test::{
2828} ;
2929use matrix_sdk_ui:: timeline:: { EventSendState , RoomExt , TimelineItemContent , VirtualTimelineItem } ;
3030use ruma:: {
31- events:: room:: message:: RoomMessageEventContent , room_id, user_id, MilliSecondsSinceUnixEpoch ,
31+ event_id, events:: room:: message:: RoomMessageEventContent , room_id, user_id,
32+ MilliSecondsSinceUnixEpoch ,
3233} ;
3334use serde_json:: json;
3435use wiremock:: {
@@ -442,3 +443,67 @@ async fn test_sync_highlighted() {
442443 // `m.room.tombstone` should be highlighted by default.
443444 assert ! ( remote_event. is_highlighted( ) ) ;
444445}
446+
447+ #[ async_test]
448+ async fn test_duplicate_maintains_correct_order ( ) {
449+ let room_id = room_id ! ( "!a98sd12bjh:example.org" ) ;
450+ let ( client, server) = logged_in_client_with_server ( ) . await ;
451+ let sync_settings = SyncSettings :: new ( ) . timeout ( Duration :: from_millis ( 3000 ) ) ;
452+
453+ let mut sync_builder = SyncResponseBuilder :: new ( ) ;
454+ sync_builder. add_joined_room ( JoinedRoomBuilder :: new ( room_id) ) ;
455+
456+ mock_sync ( & server, sync_builder. build_json_sync_response ( ) , None ) . await ;
457+ let _response = client. sync_once ( sync_settings. clone ( ) ) . await . unwrap ( ) ;
458+ server. reset ( ) . await ;
459+
460+ let room = client. get_room ( room_id) . unwrap ( ) ;
461+ let timeline = room. timeline ( ) . await . unwrap ( ) ;
462+
463+ // At the beginning, the timeline is empty.
464+ assert ! ( timeline. items( ) . await . is_empty( ) ) ;
465+
466+ let f = EventFactory :: new ( ) . sender ( user_id ! ( "@a:b.c" ) ) ;
467+
468+ // We receive an event F, from a sliding sync with timeline limit=1.
469+ sync_builder. add_joined_room (
470+ JoinedRoomBuilder :: new ( room_id)
471+ . add_timeline_event ( f. text_msg ( "C" ) . event_id ( event_id ! ( "$c" ) ) . into_raw_sync ( ) ) ,
472+ ) ;
473+
474+ mock_sync ( & server, sync_builder. build_json_sync_response ( ) , None ) . await ;
475+ let _response = client. sync_once ( sync_settings. clone ( ) ) . await . unwrap ( ) ;
476+ server. reset ( ) . await ;
477+
478+ // The timeline item represents the message we just received.
479+ let items = timeline. items ( ) . await ;
480+ assert_eq ! ( items. len( ) , 2 ) ;
481+
482+ assert ! ( items[ 0 ] . is_day_divider( ) ) ;
483+ let content = items[ 1 ] . as_event ( ) . unwrap ( ) . content ( ) . as_message ( ) . unwrap ( ) . body ( ) ;
484+ assert_eq ! ( content, "C" ) ;
485+
486+ // We receive multiple events, and C is now the last one (because we supposedly
487+ // increased the timeline limit).
488+ sync_builder. add_joined_room (
489+ JoinedRoomBuilder :: new ( room_id)
490+ . add_timeline_event ( f. text_msg ( "A" ) . event_id ( event_id ! ( "$a" ) ) . into_raw_sync ( ) )
491+ . add_timeline_event ( f. text_msg ( "B" ) . event_id ( event_id ! ( "$b" ) ) . into_raw_sync ( ) )
492+ . add_timeline_event ( f. text_msg ( "C" ) . event_id ( event_id ! ( "$c" ) ) . into_raw_sync ( ) ) ,
493+ ) ;
494+
495+ mock_sync ( & server, sync_builder. build_json_sync_response ( ) , None ) . await ;
496+ let _response = client. sync_once ( sync_settings. clone ( ) ) . await . unwrap ( ) ;
497+ server. reset ( ) . await ;
498+
499+ let items = timeline. items ( ) . await ;
500+ assert_eq ! ( items. len( ) , 4 , "{items:?}" ) ;
501+
502+ assert ! ( items[ 0 ] . is_day_divider( ) ) ;
503+ let content = items[ 1 ] . as_event ( ) . unwrap ( ) . content ( ) . as_message ( ) . unwrap ( ) . body ( ) ;
504+ assert_eq ! ( content, "A" ) ;
505+ let content = items[ 2 ] . as_event ( ) . unwrap ( ) . content ( ) . as_message ( ) . unwrap ( ) . body ( ) ;
506+ assert_eq ! ( content, "B" ) ;
507+ let content = items[ 3 ] . as_event ( ) . unwrap ( ) . content ( ) . as_message ( ) . unwrap ( ) . body ( ) ;
508+ assert_eq ! ( content, "C" ) ;
509+ }
0 commit comments