aboutsummaryrefslogtreecommitdiff
path: root/roomserver/roomserver_test.go
diff options
context:
space:
mode:
authorNeil <neilalexanderr@gmail.com>2023-01-19 20:02:32 +0000
committerGitHub <noreply@github.com>2023-01-19 21:02:32 +0100
commit738686ae686004c5efa9fe2096502cdc426c6dd8 (patch)
treee5c8e31aea47167be61aa326982ad9db1a00c15e /roomserver/roomserver_test.go
parent67f5c5bc1e837bbdee14d7d3388984ed8960528a (diff)
Add `/_dendrite/admin/purgeRoom/{roomID}` (#2662)
This adds a new admin endpoint `/_dendrite/admin/purgeRoom/{roomID}`. It completely erases all database entries for a given room ID. The roomserver will start by clearing all data for that room and then will generate an output event to notify downstream components (i.e. the sync API and federation API) to do the same. It does not currently clear media and it is currently not implemented for SQLite since it relies on SQL array operations right now. Co-authored-by: Neil Alexander <neilalexander@users.noreply.github.com> Co-authored-by: Till Faelligen <2353100+S7evinK@users.noreply.github.com>
Diffstat (limited to 'roomserver/roomserver_test.go')
-rw-r--r--roomserver/roomserver_test.go165
1 files changed, 165 insertions, 0 deletions
diff --git a/roomserver/roomserver_test.go b/roomserver/roomserver_test.go
index 595ceb52..3ec2560d 100644
--- a/roomserver/roomserver_test.go
+++ b/roomserver/roomserver_test.go
@@ -14,6 +14,10 @@ import (
userAPI "github.com/matrix-org/dendrite/userapi/api"
+ "github.com/matrix-org/dendrite/federationapi"
+ "github.com/matrix-org/dendrite/keyserver"
+ "github.com/matrix-org/dendrite/setup/jetstream"
+ "github.com/matrix-org/dendrite/syncapi"
"github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/dendrite/roomserver"
@@ -223,3 +227,164 @@ func Test_QueryLeftUsers(t *testing.T) {
})
}
+
+func TestPurgeRoom(t *testing.T) {
+ alice := test.NewUser(t)
+ bob := test.NewUser(t)
+ room := test.NewRoom(t, alice, test.RoomPreset(test.PresetTrustedPrivateChat))
+
+ // Invite Bob
+ inviteEvent := room.CreateAndInsert(t, alice, gomatrixserverlib.MRoomMember, map[string]interface{}{
+ "membership": "invite",
+ }, test.WithStateKey(bob.ID))
+
+ ctx := context.Background()
+
+ test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
+ base, db, close := mustCreateDatabase(t, dbType)
+ defer close()
+
+ jsCtx, _ := base.NATS.Prepare(base.ProcessContext, &base.Cfg.Global.JetStream)
+ defer jetstream.DeleteAllStreams(jsCtx, &base.Cfg.Global.JetStream)
+
+ fedClient := base.CreateFederationClient()
+ rsAPI := roomserver.NewInternalAPI(base)
+ keyAPI := keyserver.NewInternalAPI(base, &base.Cfg.KeyServer, fedClient, rsAPI)
+ userAPI := userapi.NewInternalAPI(base, &base.Cfg.UserAPI, nil, keyAPI, rsAPI, nil)
+
+ // this starts the JetStream consumers
+ syncapi.AddPublicRoutes(base, userAPI, rsAPI, keyAPI)
+ federationapi.NewInternalAPI(base, fedClient, rsAPI, base.Caches, nil, true)
+ rsAPI.SetFederationAPI(nil, nil)
+
+ // Create the room
+ if err := api.SendEvents(ctx, rsAPI, api.KindNew, room.Events(), "test", "test", "test", nil, false); err != nil {
+ t.Fatalf("failed to send events: %v", err)
+ }
+
+ // some dummy entries to validate after purging
+ publishResp := &api.PerformPublishResponse{}
+ if err := rsAPI.PerformPublish(ctx, &api.PerformPublishRequest{RoomID: room.ID, Visibility: "public"}, publishResp); err != nil {
+ t.Fatal(err)
+ }
+ if publishResp.Error != nil {
+ t.Fatal(publishResp.Error)
+ }
+
+ isPublished, err := db.GetPublishedRoom(ctx, room.ID)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !isPublished {
+ t.Fatalf("room should be published before purging")
+ }
+
+ aliasResp := &api.SetRoomAliasResponse{}
+ if err = rsAPI.SetRoomAlias(ctx, &api.SetRoomAliasRequest{RoomID: room.ID, Alias: "myalias", UserID: alice.ID}, aliasResp); err != nil {
+ t.Fatal(err)
+ }
+ // check the alias is actually there
+ aliasesResp := &api.GetAliasesForRoomIDResponse{}
+ if err = rsAPI.GetAliasesForRoomID(ctx, &api.GetAliasesForRoomIDRequest{RoomID: room.ID}, aliasesResp); err != nil {
+ t.Fatal(err)
+ }
+ wantAliases := 1
+ if gotAliases := len(aliasesResp.Aliases); gotAliases != wantAliases {
+ t.Fatalf("expected %d aliases, got %d", wantAliases, gotAliases)
+ }
+
+ // validate the room exists before purging
+ roomInfo, err := db.RoomInfo(ctx, room.ID)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if roomInfo == nil {
+ t.Fatalf("room does not exist")
+ }
+ // remember the roomInfo before purging
+ existingRoomInfo := roomInfo
+
+ // validate there is an invite for bob
+ nids, err := db.EventStateKeyNIDs(ctx, []string{bob.ID})
+ if err != nil {
+ t.Fatal(err)
+ }
+ bobNID, ok := nids[bob.ID]
+ if !ok {
+ t.Fatalf("%s does not exist", bob.ID)
+ }
+
+ _, inviteEventIDs, _, err := db.GetInvitesForUser(ctx, roomInfo.RoomNID, bobNID)
+ if err != nil {
+ t.Fatal(err)
+ }
+ wantInviteCount := 1
+ if inviteCount := len(inviteEventIDs); inviteCount != wantInviteCount {
+ t.Fatalf("expected there to be only %d invite events, got %d", wantInviteCount, inviteCount)
+ }
+ if inviteEventIDs[0] != inviteEvent.EventID() {
+ t.Fatalf("expected invite event ID %s, got %s", inviteEvent.EventID(), inviteEventIDs[0])
+ }
+
+ // purge the room from the database
+ purgeResp := &api.PerformAdminPurgeRoomResponse{}
+ if err = rsAPI.PerformAdminPurgeRoom(ctx, &api.PerformAdminPurgeRoomRequest{RoomID: room.ID}, purgeResp); err != nil {
+ t.Fatal(err)
+ }
+
+ // wait for all consumers to process the purge event
+ var sum = 1
+ timeout := time.Second * 5
+ deadline, cancel := context.WithTimeout(context.Background(), timeout)
+ defer cancel()
+ for sum > 0 {
+ if deadline.Err() != nil {
+ t.Fatalf("test timed out after %s", timeout)
+ }
+ sum = 0
+ consumerCh := jsCtx.Consumers(base.Cfg.Global.JetStream.Prefixed(jetstream.OutputRoomEvent))
+ for x := range consumerCh {
+ sum += x.NumAckPending
+ }
+ time.Sleep(time.Millisecond)
+ }
+
+ roomInfo, err = db.RoomInfo(ctx, room.ID)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if roomInfo != nil {
+ t.Fatalf("room should not exist after purging: %+v", roomInfo)
+ }
+
+ // validation below
+
+ // There should be no invite left
+ _, inviteEventIDs, _, err = db.GetInvitesForUser(ctx, existingRoomInfo.RoomNID, bobNID)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if inviteCount := len(inviteEventIDs); inviteCount > 0 {
+ t.Fatalf("expected there to be only %d invite events, got %d", wantInviteCount, inviteCount)
+ }
+
+ // aliases should be deleted
+ aliases, err := db.GetAliasesForRoomID(ctx, room.ID)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if aliasCount := len(aliases); aliasCount > 0 {
+ t.Fatalf("expected there to be only %d invite events, got %d", 0, aliasCount)
+ }
+
+ // published room should be deleted
+ isPublished, err = db.GetPublishedRoom(ctx, room.ID)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if isPublished {
+ t.Fatalf("room should not be published after purging")
+ }
+ })
+}