From d6e3cb0b3a29267807168b5e4d02d45da128434d Mon Sep 17 00:00:00 2001 From: Sean Quah Date: Sat, 18 Feb 2023 15:34:03 +0000 Subject: [PATCH 1/3] Define custom Server type for partial state join tests --- ...federation_room_join_partial_state_test.go | 101 ++++++++++-------- 1 file changed, 54 insertions(+), 47 deletions(-) diff --git a/tests/federation_room_join_partial_state_test.go b/tests/federation_room_join_partial_state_test.go index 9ed3d78b..d124f5ed 100644 --- a/tests/federation_room_join_partial_state_test.go +++ b/tests/federation_room_join_partial_state_test.go @@ -35,31 +35,38 @@ import ( "github.com/matrix-org/complement/internal/must" ) -func TestPartialStateJoin(t *testing.T) { - // createTestServer spins up a federation server suitable for the tests in this file - createTestServer := func(t *testing.T, deployment *docker.Deployment, opts ...func(*federation.Server)) *federation.Server { - t.Helper() +type server struct { + *federation.Server +} - return federation.NewServer(t, deployment, - append( - opts, // `opts` goes first so that it can override any of the following handlers - federation.HandleKeyRequests(), - federation.HandlePartialStateMakeSendJoinRequests(), - federation.HandleEventRequests(), - federation.HandleTransactionRequests( - func(e *gomatrixserverlib.Event) { - t.Fatalf("Received unexpected PDU: %s", string(e.JSON())) - }, - // the homeserver under test may send us presence when the joining user syncs - nil, - ), - )..., - ) - } +// createTestServer spins up a federation server suitable for the tests in this file +func createTestServer(t *testing.T, deployment *docker.Deployment, opts ...func(*federation.Server)) *server { + t.Helper() + server := &server{} + server.Server = federation.NewServer(t, deployment, + append( + opts, + federation.HandleKeyRequests(), + federation.HandlePartialStateMakeSendJoinRequests(), + federation.HandleEventRequests(), + federation.HandleTransactionRequests( + func(e *gomatrixserverlib.Event) { + t.Fatalf("Received unexpected PDU: %s", string(e.JSON())) + }, + // the homeserver under test may send us presence when the joining user syncs + nil, + ), + )..., + ) + + return server +} + +func TestPartialStateJoin(t *testing.T) { // createMemberEvent creates a membership event for the given user createMembershipEvent := func( - t *testing.T, signingServer *federation.Server, room *federation.ServerRoom, userId string, + t *testing.T, signingServer *server, room *federation.ServerRoom, userId string, membership string, ) *gomatrixserverlib.Event { t.Helper() @@ -76,7 +83,7 @@ func TestPartialStateJoin(t *testing.T) { // createJoinEvent creates a join event for the given user createJoinEvent := func( - t *testing.T, signingServer *federation.Server, room *federation.ServerRoom, userId string, + t *testing.T, signingServer *server, room *federation.ServerRoom, userId string, ) *gomatrixserverlib.Event { t.Helper() @@ -85,7 +92,7 @@ func TestPartialStateJoin(t *testing.T) { // createLeaveEvent creates a leave event for the given user createLeaveEvent := func( - t *testing.T, signingServer *federation.Server, room *federation.ServerRoom, userId string, + t *testing.T, signingServer *server, room *federation.ServerRoom, userId string, ) *gomatrixserverlib.Event { t.Helper() @@ -94,7 +101,7 @@ func TestPartialStateJoin(t *testing.T) { // createTestRoom creates a room on the complement server suitable for many of the tests in this file // The room starts with @charlie and @derek in it - createTestRoom := func(t *testing.T, server *federation.Server, roomVer gomatrixserverlib.RoomVersion) *federation.ServerRoom { + createTestRoom := func(t *testing.T, server *server, roomVer gomatrixserverlib.RoomVersion) *federation.ServerRoom { t.Helper() // create the room on the complement server, with charlie and derek as members @@ -725,7 +732,7 @@ func TestPartialStateJoin(t *testing.T) { defer psjResult.Destroy(t) // the HS will make an /event_auth request for the event - federation.HandleEventAuthRequests()(server) + federation.HandleEventAuthRequests()(server.Server) event := psjResult.CreateMessageEvent(t, "derek", nil) t.Logf("Derek created event with ID %s", event.EventID()) @@ -762,7 +769,7 @@ func TestPartialStateJoin(t *testing.T) { t.Logf("Derek created event B with ID %s", eventB.EventID()) // the HS will make an /event_auth request for event A - federation.HandleEventAuthRequests()(server) + federation.HandleEventAuthRequests()(server.Server) // the HS will make a /get_missing_events request for the missing prev events of event B handleGetMissingEventsRequests(t, server, serverRoom, @@ -802,7 +809,7 @@ func TestPartialStateJoin(t *testing.T) { t.Logf("Derek created event B with ID %s", eventB.EventID()) // the HS will make an /event_auth request for event A - federation.HandleEventAuthRequests()(server) + federation.HandleEventAuthRequests()(server.Server) // the HS will make a /get_missing_events request for the missing prev event of event B handleGetMissingEventsRequests(t, server, serverRoom, @@ -870,7 +877,7 @@ func TestPartialStateJoin(t *testing.T) { defer psjResult.Destroy(t) // the HS will make an /event_auth request for the event - federation.HandleEventAuthRequests()(server) + federation.HandleEventAuthRequests()(server.Server) // derek sends a message into the room. event := psjResult.CreateMessageEvent(t, "derek", nil) @@ -918,7 +925,7 @@ func TestPartialStateJoin(t *testing.T) { ) // the HS will make an /event_auth request for the event - federation.HandleEventAuthRequests()(server) + federation.HandleEventAuthRequests()(server.Server) // derek sends two messages into the room. event1 := psjResult.CreateMessageEvent(t, "derek", nil) @@ -988,7 +995,7 @@ func TestPartialStateJoin(t *testing.T) { ) // the HS will make an /event_auth request for the event - federation.HandleEventAuthRequests()(server) + federation.HandleEventAuthRequests()(server.Server) // derek sends a message into the room. event := psjResult.CreateMessageEvent(t, "derek", nil) @@ -1419,7 +1426,7 @@ func TestPartialStateJoin(t *testing.T) { defer psjResult.Destroy(t) // the HS will make an /event_auth request for the event - federation.HandleEventAuthRequests()(server) + federation.HandleEventAuthRequests()(server.Server) // derek sends a state event, despite not having permission to send state. This should be rejected. badStateEvent := server.MustCreateEvent(t, serverRoom, b.Event{ @@ -1484,7 +1491,7 @@ func TestPartialStateJoin(t *testing.T) { defer cancel() // the HS will make an /event_auth request for the event - federation.HandleEventAuthRequests()(server) + federation.HandleEventAuthRequests()(server.Server) // create the room on the complement server, with charlie as the founder, and derek as a user with permission // to send state. He later leaves. @@ -1562,7 +1569,7 @@ func TestPartialStateJoin(t *testing.T) { defer cancel() // the HS will make an /event_auth request for the event - federation.HandleEventAuthRequests()(server) + federation.HandleEventAuthRequests()(server.Server) // create the room on the complement server, with charlie as the founder, derek as a user with permission // to kick users, and elsie as a bystander who has permission to send state. @@ -1757,7 +1764,7 @@ func TestPartialStateJoin(t *testing.T) { // This is permissible because testServer1 is fully joined to the room. // We can't actually use /make_join because host.docker.internal doesn't resolve, // so compute it without making any requests: - makeJoinResp, err := federation.MakeRespMakeJoin(testServer1, serverRoom, testServer2.UserID("daniel")) + makeJoinResp, err := federation.MakeRespMakeJoin(testServer1.Server, serverRoom, testServer2.UserID("daniel")) if err != nil { t.Fatalf("MakeRespMakeJoin failed : %s", err) } @@ -1930,7 +1937,7 @@ func TestPartialStateJoin(t *testing.T) { // This is permissible because testServer1 is fully joined to the room. // We can't actually use /make_knock because host.docker.internal doesn't resolve, // so compute it without making any requests: - makeKnockResp, err := federation.MakeRespMakeKnock(testServer1, serverRoom, testServer2.UserID("daniel")) + makeKnockResp, err := federation.MakeRespMakeKnock(testServer1.Server, serverRoom, testServer2.UserID("daniel")) if err != nil { t.Fatalf("MakeRespMakeKnock failed : %s", err) } @@ -1966,7 +1973,7 @@ func TestPartialStateJoin(t *testing.T) { t *testing.T, deployment *docker.Deployment, aliceLocalpart string, opts ...func(*federation.Server), ) ( - alice *client.CSAPI, server1 *federation.Server, server2 *federation.Server, + alice *client.CSAPI, server1 *server, server2 *server, deviceListUpdateChannel1 chan gomatrixserverlib.DeviceListUpdateEvent, deviceListUpdateChannel2 chan gomatrixserverlib.DeviceListUpdateEvent, room *federation.ServerRoom, cleanup func(), @@ -1980,7 +1987,7 @@ func TestPartialStateJoin(t *testing.T) { t *testing.T, deployment *docker.Deployment, deviceListUpdateChannel chan gomatrixserverlib.DeviceListUpdateEvent, opts ...func(*federation.Server), - ) *federation.Server { + ) *server { return createTestServer(t, deployment, append( opts, // `opts` goes first so that it can override any of the following handlers @@ -2194,7 +2201,7 @@ func TestPartialStateJoin(t *testing.T) { // under test joins. setupIncorrectlyAcceptedKick := func( t *testing.T, deployment *docker.Deployment, alice *client.CSAPI, - server1 *federation.Server, server2 *federation.Server, + server1 *server, server2 *server, deviceListUpdateChannel1 chan gomatrixserverlib.DeviceListUpdateEvent, deviceListUpdateChannel2 chan gomatrixserverlib.DeviceListUpdateEvent, room *federation.ServerRoom, @@ -2262,7 +2269,7 @@ func TestPartialStateJoin(t *testing.T) { // Returns @alice:hs1's sync token after @elsie:server2 has left the partial state room. setupAnotherSharedRoomThenLeave := func( t *testing.T, deployment *docker.Deployment, alice *client.CSAPI, - server1 *federation.Server, server2 *federation.Server, + server1 *server, server2 *server, partialStateRoom *federation.ServerRoom, syncToken string, ) string { elsie := server2.UserID("elsie") @@ -2296,7 +2303,7 @@ func TestPartialStateJoin(t *testing.T) { // list updates once hs1's partial state join has completed. testMissedDeviceListUpdateSentOncePartialJoinCompletes := func( t *testing.T, deployment *docker.Deployment, alice *client.CSAPI, - server1 *federation.Server, server2 *federation.Server, + server1 *server, server2 *server, deviceListUpdateChannel1 chan gomatrixserverlib.DeviceListUpdateEvent, deviceListUpdateChannel2 chan gomatrixserverlib.DeviceListUpdateEvent, room *federation.ServerRoom, psjResult partialStateJoinResult, syncToken string, @@ -2445,7 +2452,7 @@ func TestPartialStateJoin(t *testing.T) { setupDeviceListCachingTest := func( t *testing.T, deployment *docker.Deployment, aliceLocalpart string, ) ( - alice *client.CSAPI, server *federation.Server, userDevicesQueryChannel chan string, + alice *client.CSAPI, server *server, userDevicesQueryChannel chan string, room *federation.ServerRoom, sendDeviceListUpdate func(string), cleanup func(), ) { alice = deployment.RegisterUser(t, "hs1", aliceLocalpart, "secret", false) @@ -2976,7 +2983,7 @@ func TestPartialStateJoin(t *testing.T) { // @elsie will be discovered to be no longer in the room. setupUserIncorrectlyInRoom := func( t *testing.T, deployment *docker.Deployment, alice *client.CSAPI, - server *federation.Server, room *federation.ServerRoom, + server *server, room *federation.ServerRoom, ) (syncToken string, psjResult partialStateJoinResult) { charlie := server.UserID("charlie") derek := server.UserID("derek") @@ -4135,7 +4142,7 @@ func buildLazyLoadingSyncFilter(timelineOptions map[string]interface{}) string { // partialStateJoinResult is the result of beginPartialStateJoin type partialStateJoinResult struct { - Server *federation.Server + Server *server ServerRoom *federation.ServerRoom User *client.CSAPI fedStateIdsRequestReceivedWaiter *Waiter @@ -4149,7 +4156,7 @@ type partialStateJoinResult struct { // When this method completes, the /join request will have completed, but the // state has not yet been re-synced. To allow the re-sync to proceed, call // partialStateJoinResult.FinishStateRequest. -func beginPartialStateJoin(t *testing.T, server *federation.Server, serverRoom *federation.ServerRoom, joiningUser *client.CSAPI) partialStateJoinResult { +func beginPartialStateJoin(t *testing.T, server *server, serverRoom *federation.ServerRoom, joiningUser *client.CSAPI) partialStateJoinResult { // we store the Server and ServerRoom for the benefit of utilities like testReceiveEventDuringPartialStateJoin result := partialStateJoinResult{ Server: server, @@ -4250,7 +4257,7 @@ func (psj *partialStateJoinResult) FinishStateRequest() { // if requestReceivedWaiter is not nil, it will be Finish()ed when the request arrives. // if sendResponseWaiter is not nil, we will Wait() for it to finish before sending the response. func handleStateIdsRequests( - t *testing.T, srv *federation.Server, serverRoom *federation.ServerRoom, + t *testing.T, srv *server, serverRoom *federation.ServerRoom, eventID string, roomState []*gomatrixserverlib.Event, requestReceivedWaiter *Waiter, sendResponseWaiter *Waiter, ) { @@ -4290,7 +4297,7 @@ func handleStateIdsRequests( // if requestReceivedWaiter is not nil, it will be Finish()ed when the request arrives. // if sendResponseWaiter is not nil, we will Wait() for it to finish before sending the response. func handleStateRequests( - t *testing.T, srv *federation.Server, serverRoom *federation.ServerRoom, + t *testing.T, srv *server, serverRoom *federation.ServerRoom, eventID string, roomState []*gomatrixserverlib.Event, requestReceivedWaiter *Waiter, sendResponseWaiter *Waiter, ) { @@ -4329,7 +4336,7 @@ func handleStateRequests( // This can (currently) only handle a single `/get_missing_events` request, and the "latest_events" in the request // must match those listed in "expectedLatestEvents" (otherwise the test is failed). func handleGetMissingEventsRequests( - t *testing.T, srv *federation.Server, serverRoom *federation.ServerRoom, + t *testing.T, srv *server, serverRoom *federation.ServerRoom, expectedLatestEvents []string, eventsToReturn []*gomatrixserverlib.Event, ) { srv.Mux().HandleFunc(fmt.Sprintf("/_matrix/federation/v1/get_missing_events/%s", serverRoom.RoomID), func(w http.ResponseWriter, req *http.Request) { From b9c24c1e339b1cfb122b66942e78141fac012c19 Mon Sep 17 00:00:00 2001 From: Sean Quah Date: Sat, 18 Feb 2023 16:04:35 +0000 Subject: [PATCH 2/3] Replace `HandleTransactionRequests` usage with list of callbacks This will allow test setup and teardown to expect PDUs and EDUs independently of tests. In a future commit, test teardown will leave rooms as part of cleanup and expect to see a corresponding leave event. --- ...federation_room_join_partial_state_test.go | 276 +++++++++--------- 1 file changed, 139 insertions(+), 137 deletions(-) diff --git a/tests/federation_room_join_partial_state_test.go b/tests/federation_room_join_partial_state_test.go index d124f5ed..22d1b3f4 100644 --- a/tests/federation_room_join_partial_state_test.go +++ b/tests/federation_room_join_partial_state_test.go @@ -37,13 +37,27 @@ import ( type server struct { *federation.Server + + pduHandlers map[int]func(*gomatrixserverlib.Event) bool + eduHandlers map[int]func(gomatrixserverlib.EDU) bool + + nextPDUHandlerKey int + nextEDUHandlerKey int } // createTestServer spins up a federation server suitable for the tests in this file +// +// The `federation.HandleTransactionRequests` handler must not be used. +// Instead, `AddPDUHandler` and `AddEDUHandler` should be used. func createTestServer(t *testing.T, deployment *docker.Deployment, opts ...func(*federation.Server)) *server { t.Helper() - server := &server{} + server := &server{ + pduHandlers: map[int]func(*gomatrixserverlib.Event) bool{}, + eduHandlers: map[int]func(gomatrixserverlib.EDU) bool{}, + nextPDUHandlerKey: 0, + nextEDUHandlerKey: 0, + } server.Server = federation.NewServer(t, deployment, append( opts, @@ -52,17 +66,61 @@ func createTestServer(t *testing.T, deployment *docker.Deployment, opts ...func( federation.HandleEventRequests(), federation.HandleTransactionRequests( func(e *gomatrixserverlib.Event) { - t.Fatalf("Received unexpected PDU: %s", string(e.JSON())) + expected := false + for _, pduHandler := range server.pduHandlers { + expected = pduHandler(e) || expected + } + + if !expected { + t.Errorf("Received unexpected PDU: %s", string(e.JSON())) + } + }, + func(edu gomatrixserverlib.EDU) { + expected := false + for _, eduHandler := range server.eduHandlers { + expected = eduHandler(edu) || expected + } + + if !expected { + t.Errorf("Received unexpected EDU: %s: %s", edu.Type, string(edu.Content)) + } }, - // the homeserver under test may send us presence when the joining user syncs - nil, ), )..., ) + // the homeserver under test may send us presence when the joining user syncs + server.AddEDUHandler(func(edu gomatrixserverlib.EDU) bool { return edu.Type == "m.presence" }) + return server } +// AddPDUHandler adds a PDU callback that returns `true` if it expected the given PDU. +// When a PDU is received which is not expected by any PDU callback, the ongoing test is failed. +// Returns a function to remove the PDU callback. +func (s *server) AddPDUHandler(pduHandler func(*gomatrixserverlib.Event) bool) func() { + pduHandlerKey := s.nextPDUHandlerKey + s.nextPDUHandlerKey++ + s.pduHandlers[pduHandlerKey] = pduHandler + + return func() { + delete(s.pduHandlers, pduHandlerKey) + } +} + +// AddEDUHandler adds an EDU callback that returns `true` if it expected the given EDU. +// When an EDU is received which is not expected by any EDU callback, the ongoing test is failed. +// Returns a function to remove the EDU callback. +func (s *server) AddEDUHandler(eduHandler func(gomatrixserverlib.EDU) bool) func() { + eduHandlerKey := s.nextEDUHandlerKey + s.nextEDUHandlerKey++ + s.eduHandlers[eduHandlerKey] = eduHandler + + return func() { + delete(s.eduHandlers, eduHandlerKey) + } +} + func TestPartialStateJoin(t *testing.T) { // createMemberEvent creates a membership event for the given user createMembershipEvent := func( @@ -176,15 +234,7 @@ func TestPartialStateJoin(t *testing.T) { _, eagerSyncToken = alice.MustSync(t, client.SyncReq{}) t.Log("1. Partial join Alice to a remote room.") - server := createTestServer( - t, - deployment, - // Allow PDUs and EDUs, since Alice will send a message in the room. - federation.HandleTransactionRequests( - func(e *gomatrixserverlib.Event) {}, - func(e gomatrixserverlib.EDU) {}, - ), - ) + server := createTestServer(t, deployment) cancel := server.Listen() defer cancel() serverRoom := createTestRoom(t, server, alice.GetDefaultRoomVersion(t)) @@ -212,6 +262,8 @@ func TestPartialStateJoin(t *testing.T) { ) t.Log("4. Have Alice send a message to the remote room.") + removePDUHandler := server.AddPDUHandler(func(*gomatrixserverlib.Event) bool { return true }) + defer removePDUHandler() messageId := alice.SendEventUnsynced(t, serverRoom.RoomID, b.Event{ Type: "m.room.message", Content: map[string]interface{}{ @@ -366,28 +418,23 @@ func TestPartialStateJoin(t *testing.T) { t.Run("CanSendEventsDuringPartialStateJoin", func(t *testing.T) { alice := deployment.RegisterUser(t, "hs1", "t3alice", "secret", false) - pdusChannel := make(chan *gomatrixserverlib.Event) - server := createTestServer( - t, - deployment, - federation.HandleTransactionRequests( - func(e *gomatrixserverlib.Event) { - pdusChannel <- e - }, - // we don't expect EDUs - func(e gomatrixserverlib.EDU) { - if e.Type != "m.presence" { - t.Fatalf("Received unexpected EDU: %s", e.Content) - } - }, - ), - ) + server := createTestServer(t, deployment) cancel := server.Listen() defer cancel() serverRoom := createTestRoom(t, server, alice.GetDefaultRoomVersion(t)) psjResult := beginPartialStateJoin(t, server, serverRoom, alice) defer psjResult.Destroy(t) + pdusChannel := make(chan *gomatrixserverlib.Event) + removePDUHandler := server.AddPDUHandler( + func(e *gomatrixserverlib.Event) bool { + pdusChannel <- e + + return true + }, + ) + defer removePDUHandler() + alice.Client.Timeout = 2 * time.Second paths := []string{"_matrix", "client", "v3", "rooms", serverRoom.RoomID, "send", "m.room.message", "0"} res := alice.MustDoFunc(t, "PUT", paths, client.WithJSONBody(t, map[string]interface{}{ @@ -1988,30 +2035,27 @@ func TestPartialStateJoin(t *testing.T) { deviceListUpdateChannel chan gomatrixserverlib.DeviceListUpdateEvent, opts ...func(*federation.Server), ) *server { - return createTestServer(t, deployment, + server := createTestServer(t, deployment, append( opts, // `opts` goes first so that it can override any of the following handlers federation.HandleEventAuthRequests(), - federation.HandleTransactionRequests( - func(e *gomatrixserverlib.Event) { - t.Fatalf("Received unexpected PDU: %s", string(e.JSON())) - }, - func(e gomatrixserverlib.EDU) { - if e.Type == "m.presence" { - return - } - if e.Type != "m.device_list_update" { - t.Fatalf("Received unexpected EDU: %s", e) - } - - t.Logf("Complement server received m.device_list_update: %v", string(e.Content)) - var deviceListUpdate gomatrixserverlib.DeviceListUpdateEvent - json.Unmarshal(e.Content, &deviceListUpdate) - deviceListUpdateChannel <- deviceListUpdate - }, - ), )..., ) + + server.AddEDUHandler(func(edu gomatrixserverlib.EDU) bool { + if edu.Type != "m.device_list_update" { + return false + } + + t.Logf("Complement server received m.device_list_update: %v", string(edu.Content)) + var deviceListUpdate gomatrixserverlib.DeviceListUpdateEvent + json.Unmarshal(edu.Content, &deviceListUpdate) + deviceListUpdateChannel <- deviceListUpdate + + return true + }) + + return server } server1 = createDeviceListUpdateTestServer(t, deployment, deviceListUpdateChannel1, opts...) @@ -3334,11 +3378,7 @@ func TestPartialStateJoin(t *testing.T) { alice := deployment.RegisterUser(t, "hs1", "t44alice", "secret", false) bob := deployment.RegisterUser(t, "hs1", "t44bob", "secret", false) - server := createTestServer( - t, - deployment, - federation.HandleTransactionRequests(nil, nil), - ) + server := createTestServer(t, deployment) cancel := server.Listen() defer cancel() serverRoom := createTestRoom(t, server, alice.GetDefaultRoomVersion(t)) @@ -3346,6 +3386,7 @@ func TestPartialStateJoin(t *testing.T) { psjResult := beginPartialStateJoin(t, server, serverRoom, alice) defer psjResult.Destroy(t) + server.AddPDUHandler(func(e *gomatrixserverlib.Event) bool { return true }) bob.JoinRoom(t, serverRoom.RoomID, []string{server.ServerName()}) alice.MustSyncUntil(t, client.SyncReq{ @@ -3369,22 +3410,7 @@ func TestPartialStateJoin(t *testing.T) { t.Run("Can change display name during partial state join", func(t *testing.T) { alice := deployment.RegisterUser(t, "hs1", "t45alice", "secret", false) - pdusChannel := make(chan *gomatrixserverlib.Event) - server := createTestServer( - t, - deployment, - federation.HandleTransactionRequests( - func(e *gomatrixserverlib.Event) { - pdusChannel <- e - }, - // we don't expect EDUs - func(e gomatrixserverlib.EDU) { - if e.Type != "m.presence" { - t.Fatalf("Received unexpected EDU: %s", e.Content) - } - }, - ), - ) + server := createTestServer(t, deployment) cancel := server.Listen() defer cancel() serverRoom := createTestRoom(t, server, alice.GetDefaultRoomVersion(t)) @@ -3392,6 +3418,16 @@ func TestPartialStateJoin(t *testing.T) { psjResult := beginPartialStateJoin(t, server, serverRoom, alice) defer psjResult.Destroy(t) + pdusChannel := make(chan *gomatrixserverlib.Event) + removePDUHandler := server.AddPDUHandler( + func(e *gomatrixserverlib.Event) bool { + pdusChannel <- e + + return true + }, + ) + defer removePDUHandler() + alice.MustDoFunc(t, "PUT", []string{"_matrix", "client", "v3", "profile", alice.UserID, "displayname"}, @@ -3421,12 +3457,7 @@ func TestPartialStateJoin(t *testing.T) { // Before testing that leaves during resyncs are seen during resyncs, sanity // check that leaves during resyncs appear after the resync. alice := deployment.RegisterUser(t, "hs1", "t42alice", "secret", false) - handleTransactions := federation.HandleTransactionRequests( - // Accept all PDUs and EDUs - func(e *gomatrixserverlib.Event) {}, - func(e gomatrixserverlib.EDU) {}, - ) - server := createTestServer(t, deployment, handleTransactions) + server := createTestServer(t, deployment) cancel := server.Listen() defer cancel() @@ -3444,6 +3475,7 @@ func TestPartialStateJoin(t *testing.T) { leaveCompleted := NewWaiter() t.Log("Alice starts a leave request") + server.AddPDUHandler(func(e *gomatrixserverlib.Event) bool { return true }) go func() { alice.LeaveRoom(t, serverRoom.RoomID) t.Log("Alice's leave request completed") @@ -3465,33 +3497,8 @@ func TestPartialStateJoin(t *testing.T) { }) t.Run("does not wait for resync", func(t *testing.T) { - // Prepare to listen for leave events from the HS under test. - // We're only expecting one leave event, but give the channel extra capacity - // to avoid deadlock if the HS does something silly. - leavesChannel := make(chan *gomatrixserverlib.Event, 10) - handleTransactions := federation.HandleTransactionRequests( - func(e *gomatrixserverlib.Event) { - if e.Type() == "m.room.member" { - if ok := gjson.ValidBytes(e.Content()); !ok { - t.Fatalf("Received event %s with invalid content: %v", e.EventID(), e.Content()) - } - content := gjson.ParseBytes(e.Content()) - membership := content.Get("membership") - if membership.Exists() && membership.Str == "leave" { - leavesChannel <- e - } - } - }, - // we don't care about EDUs - func(e gomatrixserverlib.EDU) {}, - ) - alice := deployment.RegisterUser(t, "hs1", "t43alice", "secret", false) - server := createTestServer( - t, - deployment, - handleTransactions, - ) + server := createTestServer(t, deployment) cancel := server.Listen() defer cancel() @@ -3516,6 +3523,26 @@ func TestPartialStateJoin(t *testing.T) { ) t.Logf("Alice's leave is received by the resident server") + // Prepare to listen for leave events from the HS under test. + // We're only expecting one leave event, but give the channel extra capacity + // to avoid deadlock if the HS does something silly. + leavesChannel := make(chan *gomatrixserverlib.Event, 10) + server.AddPDUHandler( + func(e *gomatrixserverlib.Event) bool { + if e.Type() == "m.room.member" { + if ok := gjson.ValidBytes(e.Content()); !ok { + t.Fatalf("Received event %s with invalid content: %v", e.EventID(), e.Content()) + } + content := gjson.ParseBytes(e.Content()) + membership := content.Get("membership") + if membership.Exists() && membership.Str == "leave" { + leavesChannel <- e + } + } + + return true + }, + ) select { case <-time.After(1 * time.Second): t.Fatal("Resident server did not receive Alice's leave") @@ -3530,12 +3557,7 @@ func TestPartialStateJoin(t *testing.T) { t.Run("works after a second partial join", func(t *testing.T) { alice := deployment.RegisterUser(t, "hs1", "t47alice", "secret", false) bob := deployment.RegisterUser(t, "hs1", "t47bob", "secret", false) - handleTransactions := federation.HandleTransactionRequests( - // Accept all PDUs and EDUs - func(e *gomatrixserverlib.Event) {}, - func(e gomatrixserverlib.EDU) {}, - ) - server := createTestServer(t, deployment, handleTransactions) + server := createTestServer(t, deployment) cancel := server.Listen() defer cancel() @@ -3565,6 +3587,7 @@ func TestPartialStateJoin(t *testing.T) { ) t.Log("Alice leaves the room") + server.AddPDUHandler(func(e *gomatrixserverlib.Event) bool { return true }) alice.LeaveRoom(t, serverRoom.RoomID) t.Log("Alice sees Alice's leave") @@ -3584,12 +3607,7 @@ func TestPartialStateJoin(t *testing.T) { t.Run("succeeds, then rejoin succeeds without resync completing", func(t *testing.T) { alice := deployment.RegisterUser(t, "hs1", "t48alice", "secret", false) - handleTransactions := federation.HandleTransactionRequests( - // Accept all PDUs and EDUs - func(e *gomatrixserverlib.Event) {}, - func(e gomatrixserverlib.EDU) {}, - ) - server := createTestServer(t, deployment, handleTransactions) + server := createTestServer(t, deployment) cancel := server.Listen() defer cancel() @@ -3606,6 +3624,7 @@ func TestPartialStateJoin(t *testing.T) { ) t.Log("Alice leaves the room") + server.AddPDUHandler(func(e *gomatrixserverlib.Event) bool { return true }) alice.LeaveRoom(t, serverRoom.RoomID) t.Log("Alice sees Alice's leave") @@ -3629,12 +3648,7 @@ func TestPartialStateJoin(t *testing.T) { t.Run("succeeds, then another user can join without resync completing", func(t *testing.T) { alice := deployment.RegisterUser(t, "hs1", "t49alice", "secret", false) bob := deployment.RegisterUser(t, "hs1", "t49bob", "secret", false) - handleTransactions := federation.HandleTransactionRequests( - // Accept all PDUs and EDUs - func(e *gomatrixserverlib.Event) {}, - func(e gomatrixserverlib.EDU) {}, - ) - server := createTestServer(t, deployment, handleTransactions) + server := createTestServer(t, deployment) cancel := server.Listen() defer cancel() @@ -3654,6 +3668,7 @@ func TestPartialStateJoin(t *testing.T) { ) t.Log("Alice leaves the room") + server.AddPDUHandler(func(e *gomatrixserverlib.Event) bool { return true }) alice.LeaveRoom(t, serverRoom.RoomID) t.Log("Alice sees Alice's leave") @@ -3676,12 +3691,7 @@ func TestPartialStateJoin(t *testing.T) { t.Run("can be triggered by remote kick", func(t *testing.T) { alice := deployment.RegisterUser(t, "hs1", "t50alice", "secret", false) - handleTransactions := federation.HandleTransactionRequests( - // Accept all PDUs and EDUs - func(e *gomatrixserverlib.Event) {}, - func(e gomatrixserverlib.EDU) {}, - ) - server := createTestServer(t, deployment, handleTransactions) + server := createTestServer(t, deployment) cancel := server.Listen() defer cancel() @@ -3729,12 +3739,7 @@ func TestPartialStateJoin(t *testing.T) { t.Run("can be triggered by remote ban", func(t *testing.T) { alice := deployment.RegisterUser(t, "hs1", "t51alice", "secret", false) - handleTransactions := federation.HandleTransactionRequests( - // Accept all PDUs and EDUs - func(e *gomatrixserverlib.Event) {}, - func(e gomatrixserverlib.EDU) {}, - ) - server := createTestServer(t, deployment, handleTransactions) + server := createTestServer(t, deployment) cancel := server.Listen() defer cancel() @@ -3908,12 +3913,7 @@ func TestPartialStateJoin(t *testing.T) { } t.Log("Alice begins a partial join to a room") alice := deployment.RegisterUser(t, "hs1", "t46alice", "secret", true) - // Ignore PDUs (leaves from shutting down the room) and EDUs (presence). - server := createTestServer( - t, - deployment, - federation.HandleTransactionRequests(nil, nil), - ) + server := createTestServer(t, deployment) cancel := server.Listen() defer cancel() @@ -3942,6 +3942,8 @@ func TestPartialStateJoin(t *testing.T) { psjResult.AwaitStateIdsRequest(t) t.Log("Alice purges that room") + // Ignore PDUs (leaves from shutting down the room). + server.AddPDUHandler(func(e *gomatrixserverlib.Event) bool { return true }) alice.MustDoFunc(t, "DELETE", []string{"_synapse", "admin", "v1", "rooms", serverRoom.RoomID}, client.WithJSONBody(t, map[string]interface{}{})) // Note: clients don't get told about purged rooms. No leave event for you! From 0543eb71e39201a486f2ebcf5f1685bb3df36f7a Mon Sep 17 00:00:00 2001 From: Sean Quah Date: Tue, 14 Mar 2023 16:17:11 +0000 Subject: [PATCH 3/3] Leave rooms in test cleanup Make the homeserver under test leave test rooms at the end of each test, so that it stops trying to contact other homeservers in the room. Signed-off-by: Sean Quah --- ...federation_room_join_partial_state_test.go | 55 ++++++++++++++++++- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/tests/federation_room_join_partial_state_test.go b/tests/federation_room_join_partial_state_test.go index 22d1b3f4..f66a56ae 100644 --- a/tests/federation_room_join_partial_state_test.go +++ b/tests/federation_room_join_partial_state_test.go @@ -121,6 +121,44 @@ func (s *server) AddEDUHandler(eduHandler func(gomatrixserverlib.EDU) bool) func } } +// WithWaitForLeave runs the given action and waits for the user to leave the room. +func (s *server) WithWaitForLeave( + t *testing.T, room *federation.ServerRoom, userID string, leaveAction func(), +) { + leaveChannel := make(chan *gomatrixserverlib.Event, 10) + removePDUHandler := s.AddPDUHandler( + func(e *gomatrixserverlib.Event) bool { + if membership, _ := e.Membership(); e.Type() == "m.room.member" && + *e.StateKey() == userID && + membership == "leave" { + leaveChannel <- e + return true + } + return false + }, + ) + defer removePDUHandler() + + leaveAction() + + memberEvent := room.CurrentState("m.room.member", userID) + membership := "" + if memberEvent != nil { + membership, _ = memberEvent.Membership() + } + if membership == "leave" { + t.Logf("%s has already seen %s leave test room %s.", s.ServerName(), userID, room.RoomID) + } else { + select { + case <-leaveChannel: + t.Logf("%s saw %s leave test room %s.", s.ServerName(), userID, room.RoomID) + break + case <-time.After(1 * time.Second): + t.Errorf("%s timed out waiting for %s to leave test room %s.", s.ServerName(), userID, room.RoomID) + } + } +} + func TestPartialStateJoin(t *testing.T) { // createMemberEvent creates a membership event for the given user createMembershipEvent := func( @@ -4202,8 +4240,10 @@ func beginPartialStateJoin(t *testing.T, server *server, serverRoom *federation. return result } -// Destroy cleans up the resources associated with the join attempt. It must -// be called once the test is finished +// Destroy cleans up the resources associated with the join attempt. +// It is idempotent and must be called once the test is finished. +// Specifically, it ensures that the partial state join completes and makes the joining user leave +// the room. func (psj *partialStateJoinResult) Destroy(t *testing.T) { if psj.fedStateIdsSendResponseWaiter != nil { psj.fedStateIdsSendResponseWaiter.Finish() @@ -4217,7 +4257,18 @@ func (psj *partialStateJoinResult) Destroy(t *testing.T) { // has finished all federation activity before tearing down the Complement server. // Otherwise the homeserver at the Complement's hostname:port combination may be // considered offline and interfere with subsequent tests. + t.Log("Cleaning up after test...") + awaitPartialStateJoinCompletion(t, psj.ServerRoom, psj.User) + + // The caller is about to tear down the Complement homeserver. Leave the room, so + // that the homeserver under test stops sending it presence updates. + psj.Server.WithWaitForLeave( + t, + psj.ServerRoom, + psj.User.UserID, + func() { psj.User.LeaveRoom(t, psj.ServerRoom.RoomID) }, + ) } // send a message into the room without letting the homeserver under test know about it.