11package tests
22
33import (
4- "encoding/json"
54 "fmt"
65 "net/url"
76 "strconv"
@@ -13,13 +12,11 @@ import (
1312 "github.com/matrix-org/complement/b"
1413 "github.com/matrix-org/complement/client"
1514 "github.com/matrix-org/complement/ct"
16- "github.com/matrix-org/complement/federation"
1715 "github.com/matrix-org/complement/helpers"
1816 "github.com/matrix-org/complement/match"
1917 "github.com/matrix-org/complement/must"
2018 "github.com/matrix-org/gomatrixserverlib/spec"
2119 "github.com/tidwall/gjson"
22- "github.com/tidwall/sjson"
2320)
2421
2522var txnID int64 = 10000
@@ -53,6 +50,7 @@ func sendStickyEvent(t ct.TestLike, c *client.CSAPI, roomID string, e b.Event, o
5350}
5451
5552func MustDoSlidingSync (t ct.TestLike , user * client.CSAPI , pos string ) (gjson.Result , string ) {
53+ t .Helper ()
5654 body := map [string ]interface {}{
5755 "lists" : map [string ]any {
5856 "any-key" : map [string ]any {
@@ -111,8 +109,9 @@ var stopMsg = b.Event{
111109
112110// Helper function to do /sync or SSS requests. Does a single /sync request.
113111// Returns the sticky/timeline events for the provided room ID, if any.
114- // Returns `true` if the timeline included stopAtEventID.
112+ // Returns `true` if the timeline or sticky section included stopAtEventID.
115113func performSync (t ct.TestLike , cli * client.CSAPI , useSimplifiedSlidingSync bool , since , roomID , stopAtEventID string ) (syncResp syncResponse , nextSince string , stop bool ) {
114+ t .Helper ()
116115 var timeline []gjson.Result
117116 var sticky []gjson.Result
118117 var resp gjson.Result
@@ -126,7 +125,7 @@ func performSync(t ct.TestLike, cli *client.CSAPI, useSimplifiedSlidingSync bool
126125 sticky = resp .Get ("rooms.join." + client .GjsonEscape (roomID ) + ".msc4354_sticky.events" ).Array ()
127126 // t.Logf("%s\b", resp.Raw)
128127 }
129- for _ , ev := range timeline {
128+ for _ , ev := range append ( append ([]gjson. Result {}, timeline ... ), sticky ... ) {
130129 if ev .Get ("event_id" ).Str == stopAtEventID {
131130 stop = true
132131 break
@@ -407,79 +406,127 @@ func TestStickyEventsIgnoreHistoryVisibility(t *testing.T) {
407406 })
408407}
409408
410- func xTestSoftFailedStickyEvents (t * testing.T ) {
411- deployment := complement .Deploy (t , 1 )
409+ func xTestStickyEventsSentToNewlyJoinedServers (t * testing.T ) {
410+ deployment := complement .Deploy (t , 3 )
412411 defer deployment .Destroy (t )
413412
414- srv := federation .NewServer (t , deployment ,
415- federation .HandleKeyRequests (),
416- federation .HandleMakeSendJoinRequests (),
417- federation .HandleTransactionRequests (
418- nil , nil ,
419- ),
420- )
421- cancel := srv .Listen ()
422- defer cancel ()
413+ // newJoiner will join via alice (hs1).
414+ // we include bob as a bystander server. hs2 will not process the /send_join response
415+ // but should receive the join event and realise it needs to send its own sticky events
416+ // to hs3.
417+ alice := deployment .Register (t , "hs1" , helpers.RegistrationOpts {})
418+ bob := deployment .Register (t , "hs2" , helpers.RegistrationOpts {})
419+ newJoiner := deployment .Register (t , "hs3" , helpers.RegistrationOpts {})
420+
421+ forEachSync (t , func (t * testing.T , useSimplifiedSlidingSync bool ) {
422+ roomID := alice .MustCreateRoom (t , map [string ]interface {}{"preset" : "public_chat" })
423+ bob .MustJoinRoom (t , roomID , []spec.ServerName {"hs1" })
424+ // Make a timeline like
425+ // [ STICKY, MSG1, MSG2, ... MSG25, STICKY ]
426+ // and ensure newly joined servers see both sticky events
427+ duration := 30000
428+ aliceStickyEventIDNotInTimeline := sendStickyEvent (t , alice , roomID , b.Event {
429+ Type : "m.room.message" ,
430+ Content : map [string ]interface {}{
431+ "msgtype" : "m.text" ,
432+ "body" : "ALICE This is a sticky event which is beyond the timeline limit" ,
433+ },
434+ }, withStickyDuration (duration ))
435+ bobStickyEventIDNotInTimeline := sendStickyEvent (t , bob , roomID , b.Event {
436+ Type : "m.room.message" ,
437+ Content : map [string ]interface {}{
438+ "msgtype" : "m.text" ,
439+ "body" : "BOB This is a sticky event which is beyond the timeline limit" ,
440+ },
441+ }, withStickyDuration (duration ))
442+ for i := 0 ; i < 25 ; i ++ {
443+ alice .Unsafe_SendEventUnsynced (t , roomID , b.Event {
444+ Type : "m.room.message" ,
445+ Content : map [string ]interface {}{
446+ "msgtype" : "m.text" ,
447+ "body" : fmt .Sprintf ("msg %d" , i ),
448+ },
449+ })
450+ }
451+ aliceStickyEventIDInTimeline := sendStickyEvent (t , alice , roomID , b.Event {
452+ Type : "m.room.message" ,
453+ Content : map [string ]interface {}{
454+ "msgtype" : "m.text" ,
455+ "body" : "ALICE This is a sticky event which is inside the timeline limit" ,
456+ },
457+ }, withStickyDuration (duration ))
458+ bobStickyEventIDInTimeline := sendStickyEvent (t , bob , roomID , b.Event {
459+ Type : "m.room.message" ,
460+ Content : map [string ]interface {}{
461+ "msgtype" : "m.text" ,
462+ "body" : "BOB This is a sticky event which is inside the timeline limit" ,
463+ },
464+ }, withStickyDuration (duration ))
465+
466+ newJoiner .MustJoinRoom (t , roomID , []spec.ServerName {"hs1" })
467+
468+ // wait until hs1 and hs2 see the join, as this will trigger the sending of sticky events
469+ alice .MustSyncUntil (t , client.SyncReq {}, client .SyncJoinedTo (newJoiner .UserID , roomID ))
470+ bob .MustSyncUntil (t , client.SyncReq {}, client .SyncJoinedTo (newJoiner .UserID , roomID ))
471+
472+ stopEventID := alice .Unsafe_SendEventUnsynced (t , roomID , stopMsg )
473+
474+ syncResp := gatherSyncResults (t , newJoiner , useSimplifiedSlidingSync , roomID , stopEventID )
475+ mustHaveStickyEventID (t , aliceStickyEventIDInTimeline , syncResp .stickyEvents )
476+ mustHaveStickyEventID (t , aliceStickyEventIDNotInTimeline , syncResp .stickyEvents )
477+ mustHaveStickyEventID (t , bobStickyEventIDInTimeline , syncResp .stickyEvents )
478+ mustHaveStickyEventID (t , bobStickyEventIDNotInTimeline , syncResp .stickyEvents )
479+ })
480+ }
481+
482+ func TestSoftFailedStickyEvents (t * testing.T ) {
483+ deployment := complement .Deploy (t , 2 )
484+ defer deployment .Destroy (t )
423485
424486 alice := deployment .Register (t , "hs1" , helpers.RegistrationOpts {})
425- bob := srv .UserID ("bob" )
487+ bob := deployment .Register (t , "hs2" , helpers.RegistrationOpts {})
488+ sentinel := deployment .Register (t , "hs2" , helpers.RegistrationOpts {})
426489
427490 roomID := alice .MustCreateRoom (t , map [string ]interface {}{"preset" : "public_chat" })
428- srvRoom := srv .MustJoinRoom (t , deployment , "hs1" , roomID , bob )
429- latestEventID := srvRoom .ForwardExtremities [0 ]
430- t .Logf ("latestEventID = %s" , latestEventID )
431-
432- // Alice kicks Bob. Concurrently, Bob sends a sticky event. The sticky event is soft-failed.
491+ bob .MustJoinRoom (t , roomID , []spec.ServerName {"hs1" })
492+ sentinel .MustJoinRoom (t , roomID , []spec.ServerName {"hs1" })
493+
494+ // We want to concurrently:
495+ // - Alice kicks Bob
496+ // - Bob sends a sticky event.
497+ // To do this, we will pause each server so they can't communicate their events with each other.
498+ deployment .PauseServer (t , "hs2" )
433499 alice .MustDo (t , "POST" , []string {"_matrix" , "client" , "v3" , "rooms" , roomID , "kick" }, client .WithJSONBody (t , map [string ]string {
434- "user_id" : bob ,
500+ "user_id" : bob . UserID ,
435501 "reason" : "Testing" ,
436502 }))
437- stickyPDU := srv .MustCreateEvent (t , srvRoom , federation.Event {
438- Type : "m.room.message" ,
439- Sender : bob ,
503+ deployment .PauseServer (t , "hs1" )
504+ deployment .UnpauseServer (t , "hs2" )
505+ stickyEventID := sendStickyEvent (t , bob , roomID , b.Event {
506+ Type : "m.room.message" ,
440507 Content : map [string ]interface {}{
441508 "msgtype" : "m.text" ,
442- "body" : "Bob's sticky event" ,
443- },
444- PrevEvents : []string {latestEventID },
445- AuthEvents : []string {
446- srvRoom .CurrentState (spec .MRoomCreate , "" ).EventID (),
447- srvRoom .CurrentState (spec .MRoomPowerLevels , "" ).EventID (),
448- latestEventID , // bob's join
509+ "body" : "This is a sticky message sent whilst HS1 is offline" ,
449510 },
450511 })
451- // XXX: this doesn't work as it trips the content hash check
452- stickyJSON := stickyPDU .JSON ()
453- stickyJSON , err := sjson .SetBytes (stickyJSON , "msc4354_sticky.duration_ms" , 600000 )
454- must .NotError (t , "failed to set sticky field" , err )
455- srv .MustSendTransaction (t , deployment , "hs1" , []json.RawMessage {stickyJSON }, nil )
456- t .Logf ("sticky event ID: %s" , stickyPDU .EventID ())
457-
458- // TODO: Check that the sticky event was soft-failed and did not appear in the timeline.
459-
460- // now send 25 timeline events to shift the timeline.
461- // TODO: test without this as well, as it shouldn't matter (it'll always go to sticky even if <25 events)
462- for i := 0 ; i < 25 ; i ++ {
463- alice .Unsafe_SendEventUnsynced (t , roomID , b.Event {
464- Type : "m.room.message" ,
465- Content : map [string ]interface {}{
466- "msgtype" : "m.text" ,
467- "body" : fmt .Sprintf ("msg %d" , i ),
468- },
469- })
512+ deployment .UnpauseServer (t , "hs1" )
513+
514+ // we want to check that the sticky event was in fact soft-failed. This is hard to do since it won't
515+ // come down /sync. Instead, we send a sentinel message from a different user and assert that we see
516+ // the sentinel event but not the sticky event.
517+ sentinelEventID := sentinel .Unsafe_SendEventUnsynced (t , roomID , stopMsg )
518+ syncResp := gatherSyncResults (t , alice , false , roomID , sentinelEventID )
519+ for _ , ev := range append (syncResp .timelineEvents , syncResp .stickyEvents ... ) {
520+ if ev .Get ("event_id" ).Str == stickyEventID {
521+ ct .Fatalf (t , "sticky event %s was not soft failed!" , stickyEventID )
522+ }
470523 }
471- // now Bob rejoins. We should see the sticky event in the sticky section.
472- srv .MustJoinRoom (t , deployment , "hs1" , roomID , bob )
473524
474- stopEventID := alice . Unsafe_SendEventUnsynced ( t , roomID , b. Event {
475- Type : "m.room.message" ,
476- Content : map [ string ] interface {}{
477- "msgtype" : "m.text" ,
478- "body" : "STOP" ,
479- },
525+ // now we rejoin bob.
526+ // This should cause soft-failure of sticky events to be re-evaluated, causing it to appear in the 'sticky' section.
527+ bob . MustJoinRoom ( t , roomID , []spec. ServerName { "hs1" })
528+ forEachSync ( t , func ( t * testing. T , useSimplifiedSlidingSync bool ) {
529+ syncResp := gatherSyncResults ( t , alice , useSimplifiedSlidingSync , roomID , stickyEventID )
530+ mustHaveStickyEventID ( t , stickyEventID , syncResp . stickyEvents )
480531 })
481-
482- syncResp := gatherSyncResults (t , alice , false , roomID , stopEventID )
483- mustHaveStickyEventID (t , stickyPDU .EventID (), syncResp .stickyEvents )
484-
485532}
0 commit comments