mirror of
https://github.com/matrix-org/dendrite.git
synced 2026-01-16 10:33:11 -06:00
Add upgrade test
This commit is contained in:
parent
1c04196d74
commit
26d1e3599a
|
|
@ -48,7 +48,6 @@ type createRoomRequest struct {
|
|||
CreationContent json.RawMessage `json:"creation_content"`
|
||||
InitialState []fledglingEvent `json:"initial_state"`
|
||||
RoomAliasName string `json:"room_alias_name"`
|
||||
GuestCanJoin bool `json:"guest_can_join"`
|
||||
RoomVersion gomatrixserverlib.RoomVersion `json:"room_version"`
|
||||
PowerLevelContentOverride json.RawMessage `json:"power_level_content_override"`
|
||||
IsDirect bool `json:"is_direct"`
|
||||
|
|
@ -253,16 +252,19 @@ func createRoom(
|
|||
}
|
||||
}
|
||||
|
||||
var guestsCanJoin bool
|
||||
switch r.Preset {
|
||||
case presetPrivateChat:
|
||||
joinRuleContent.JoinRule = spec.Invite
|
||||
historyVisibilityContent.HistoryVisibility = historyVisibilityShared
|
||||
guestsCanJoin = true
|
||||
case presetTrustedPrivateChat:
|
||||
joinRuleContent.JoinRule = spec.Invite
|
||||
historyVisibilityContent.HistoryVisibility = historyVisibilityShared
|
||||
for _, invitee := range r.Invite {
|
||||
powerLevelContent.Users[invitee] = 100
|
||||
}
|
||||
guestsCanJoin = true
|
||||
case presetPublicChat:
|
||||
joinRuleContent.JoinRule = spec.Public
|
||||
historyVisibilityContent.HistoryVisibility = historyVisibilityShared
|
||||
|
|
@ -317,7 +319,7 @@ func createRoom(
|
|||
}
|
||||
}
|
||||
|
||||
if r.GuestCanJoin {
|
||||
if guestsCanJoin {
|
||||
guestAccessEvent = &fledglingEvent{
|
||||
Type: spec.MRoomGuestAccess,
|
||||
Content: eventutil.GuestAccessContent{
|
||||
|
|
|
|||
|
|
@ -66,7 +66,6 @@ func TestJoinRoomByIDOrAlias(t *testing.T) {
|
|||
Preset: presetPublicChat,
|
||||
RoomAliasName: "alias",
|
||||
Invite: []string{bob.ID},
|
||||
GuestCanJoin: false,
|
||||
}, aliceDev, &cfg.ClientAPI, userAPI, rsAPI, asAPI, time.Now())
|
||||
crResp, ok := resp.JSON.(createRoomResponse)
|
||||
if !ok {
|
||||
|
|
@ -75,13 +74,12 @@ func TestJoinRoomByIDOrAlias(t *testing.T) {
|
|||
|
||||
// create a room with guest access enabled and invite Charlie
|
||||
resp = createRoom(ctx, createRoomRequest{
|
||||
Name: "testing",
|
||||
IsDirect: true,
|
||||
Topic: "testing",
|
||||
Visibility: "public",
|
||||
Preset: presetPublicChat,
|
||||
Invite: []string{charlie.ID},
|
||||
GuestCanJoin: true,
|
||||
Name: "testing",
|
||||
IsDirect: true,
|
||||
Topic: "testing",
|
||||
Visibility: "public",
|
||||
Preset: presetPublicChat,
|
||||
Invite: []string{charlie.ID},
|
||||
}, aliceDev, &cfg.ClientAPI, userAPI, rsAPI, asAPI, time.Now())
|
||||
crRespWithGuestAccess, ok := resp.JSON.(createRoomResponse)
|
||||
if !ok {
|
||||
|
|
|
|||
|
|
@ -157,7 +157,6 @@ func SendServerNotice(
|
|||
Visibility: "private",
|
||||
Preset: presetPrivateChat,
|
||||
CreationContent: cc,
|
||||
GuestCanJoin: false,
|
||||
RoomVersion: roomVersion,
|
||||
PowerLevelContentOverride: pl,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -478,7 +478,7 @@ func (r *Inputer) processRoomEvent(
|
|||
|
||||
// If guest_access changed and is not can_join, kick all guest users.
|
||||
if event.Type() == spec.MRoomGuestAccess && gjson.GetBytes(event.Content(), "guest_access").Str != "can_join" {
|
||||
if err = r.kickGuests(ctx, event, roomInfo); err != nil {
|
||||
if err = r.kickGuests(ctx, event, roomInfo); err != nil && err != sql.ErrNoRows {
|
||||
logrus.WithError(err).Error("failed to kick guest users on m.room.guest_access revocation")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -370,6 +370,10 @@ func (r *Upgrader) generateInitialEvents(ctx context.Context, oldRoom *api.Query
|
|||
continue
|
||||
}
|
||||
}
|
||||
// skip events that rely on a specific user being present
|
||||
if event.Type() != spec.MRoomMember && !event.StateKeyEquals("") {
|
||||
continue
|
||||
}
|
||||
state[gomatrixserverlib.StateKeyTuple{EventType: event.Type(), StateKey: *event.StateKey()}] = event
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,10 +8,13 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/matrix-org/dendrite/internal/caching"
|
||||
"github.com/matrix-org/dendrite/internal/eventutil"
|
||||
"github.com/matrix-org/dendrite/internal/httputil"
|
||||
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||
"github.com/matrix-org/dendrite/roomserver/version"
|
||||
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
"github.com/matrix-org/dendrite/roomserver/state"
|
||||
"github.com/matrix-org/dendrite/roomserver/types"
|
||||
|
|
@ -754,3 +757,327 @@ func TestQueryRestrictedJoinAllowed(t *testing.T) {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestUpgrade(t *testing.T) {
|
||||
alice := test.NewUser(t)
|
||||
bob := test.NewUser(t)
|
||||
charlie := test.NewUser(t)
|
||||
ctx := context.Background()
|
||||
|
||||
validateTuples := []gomatrixserverlib.StateKeyTuple{
|
||||
{EventType: spec.MRoomCreate},
|
||||
{EventType: spec.MRoomPowerLevels},
|
||||
{EventType: spec.MRoomJoinRules},
|
||||
{EventType: spec.MRoomName},
|
||||
{EventType: spec.MRoomCanonicalAlias},
|
||||
{EventType: "m.room.tombstone"},
|
||||
{EventType: "m.custom.event"},
|
||||
{EventType: "m.custom.event", StateKey: alice.ID},
|
||||
{EventType: spec.MRoomMember, StateKey: bob.ID}, // invite should be transferred
|
||||
{EventType: spec.MRoomMember, StateKey: charlie.ID}, // ban should be transferred
|
||||
}
|
||||
|
||||
validate := func(t *testing.T, oldRoomID, newRoomID string, rsAPI api.RoomserverInternalAPI) {
|
||||
|
||||
oldRoomState := &api.QueryCurrentStateResponse{}
|
||||
if err := rsAPI.QueryCurrentState(ctx, &api.QueryCurrentStateRequest{
|
||||
RoomID: oldRoomID,
|
||||
StateTuples: validateTuples,
|
||||
}, oldRoomState); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
newRoomState := &api.QueryCurrentStateResponse{}
|
||||
if err := rsAPI.QueryCurrentState(ctx, &api.QueryCurrentStateRequest{
|
||||
RoomID: newRoomID,
|
||||
StateTuples: validateTuples,
|
||||
}, newRoomState); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// the old room should have a tombstone event
|
||||
ev := oldRoomState.StateEvents[gomatrixserverlib.StateKeyTuple{EventType: "m.room.tombstone"}]
|
||||
replacementRoom := gjson.GetBytes(ev.Content(), "replacement_room").Str
|
||||
if replacementRoom != newRoomID {
|
||||
t.Fatalf("tombstone event has replacement_room '%s', expected '%s'", replacementRoom, newRoomID)
|
||||
}
|
||||
|
||||
// the new room should have a predecessor equal to the old room
|
||||
ev = newRoomState.StateEvents[gomatrixserverlib.StateKeyTuple{EventType: spec.MRoomCreate}]
|
||||
predecessor := gjson.GetBytes(ev.Content(), "predecessor.room_id").Str
|
||||
if predecessor != oldRoomID {
|
||||
t.Fatalf("got predecessor room '%s', expected '%s'", predecessor, oldRoomID)
|
||||
}
|
||||
|
||||
for _, tuple := range validateTuples {
|
||||
// Skip create and powerlevel event (new room has e.g. predecessor event, old room has restricted powerlevels)
|
||||
switch tuple.EventType {
|
||||
case spec.MRoomCreate, spec.MRoomPowerLevels, spec.MRoomCanonicalAlias:
|
||||
continue
|
||||
}
|
||||
oldEv, ok := oldRoomState.StateEvents[tuple]
|
||||
if !ok {
|
||||
t.Logf("skipping tuple %#v as it doesn't exist in the old room", tuple)
|
||||
continue
|
||||
}
|
||||
newEv, ok := newRoomState.StateEvents[tuple]
|
||||
if !ok {
|
||||
t.Logf("skipping tuple %#v as it doesn't exist in the new room", tuple)
|
||||
continue
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(oldEv.Content(), newEv.Content()) {
|
||||
t.Logf("OldEvent QueryCurrentState: %s", string(oldEv.Content()))
|
||||
t.Logf("NewEvent QueryCurrentState: %s", string(newEv.Content()))
|
||||
t.Errorf("event content mismatch")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
upgradeUser string
|
||||
roomFunc func(rsAPI api.RoomserverInternalAPI) string
|
||||
validateFunc func(t *testing.T, oldRoomID, newRoomID string, rsAPI api.RoomserverInternalAPI)
|
||||
wantNewRoom bool
|
||||
}{
|
||||
{
|
||||
name: "invalid userID",
|
||||
upgradeUser: "!notvalid:test",
|
||||
roomFunc: func(rsAPI api.RoomserverInternalAPI) string {
|
||||
room := test.NewRoom(t, alice)
|
||||
if err := api.SendEvents(ctx, rsAPI, api.KindNew, room.Events(), "test", "test", "test", nil, false); err != nil {
|
||||
t.Errorf("failed to send events: %v", err)
|
||||
}
|
||||
return room.ID
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid roomID",
|
||||
upgradeUser: alice.ID,
|
||||
roomFunc: func(rsAPI api.RoomserverInternalAPI) string {
|
||||
return "!doesnotexist:test"
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "powerlevel too low",
|
||||
upgradeUser: bob.ID,
|
||||
roomFunc: func(rsAPI api.RoomserverInternalAPI) string {
|
||||
room := test.NewRoom(t, alice)
|
||||
if err := api.SendEvents(ctx, rsAPI, api.KindNew, room.Events(), "test", "test", "test", nil, false); err != nil {
|
||||
t.Errorf("failed to send events: %v", err)
|
||||
}
|
||||
return room.ID
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "successful upgrade on new room",
|
||||
upgradeUser: alice.ID,
|
||||
roomFunc: func(rsAPI api.RoomserverInternalAPI) string {
|
||||
room := test.NewRoom(t, alice)
|
||||
if err := api.SendEvents(ctx, rsAPI, api.KindNew, room.Events(), "test", "test", "test", nil, false); err != nil {
|
||||
t.Errorf("failed to send events: %v", err)
|
||||
}
|
||||
return room.ID
|
||||
},
|
||||
wantNewRoom: true,
|
||||
validateFunc: validate,
|
||||
},
|
||||
{
|
||||
name: "successful upgrade on new room with other state events",
|
||||
upgradeUser: alice.ID,
|
||||
roomFunc: func(rsAPI api.RoomserverInternalAPI) string {
|
||||
r := test.NewRoom(t, alice)
|
||||
r.CreateAndInsert(t, alice, spec.MRoomName, map[string]interface{}{
|
||||
"name": "my new name",
|
||||
}, test.WithStateKey(""))
|
||||
r.CreateAndInsert(t, alice, spec.MRoomCanonicalAlias, eventutil.CanonicalAliasContent{
|
||||
Alias: "#myalias:test",
|
||||
}, test.WithStateKey(""))
|
||||
|
||||
// this will be transferred
|
||||
r.CreateAndInsert(t, alice, "m.custom.event", map[string]interface{}{
|
||||
"random": "i should exist",
|
||||
}, test.WithStateKey(""))
|
||||
|
||||
// the following will be ignored
|
||||
r.CreateAndInsert(t, alice, "m.custom.event", map[string]interface{}{
|
||||
"random": "i will be ignored",
|
||||
}, test.WithStateKey(alice.ID))
|
||||
|
||||
if err := api.SendEvents(ctx, rsAPI, api.KindNew, r.Events(), "test", "test", "test", nil, false); err != nil {
|
||||
t.Errorf("failed to send events: %v", err)
|
||||
}
|
||||
return r.ID
|
||||
},
|
||||
wantNewRoom: true,
|
||||
validateFunc: validate,
|
||||
},
|
||||
{
|
||||
name: "with published room",
|
||||
upgradeUser: alice.ID,
|
||||
roomFunc: func(rsAPI api.RoomserverInternalAPI) string {
|
||||
r := test.NewRoom(t, alice)
|
||||
if err := api.SendEvents(ctx, rsAPI, api.KindNew, r.Events(), "test", "test", "test", nil, false); err != nil {
|
||||
t.Errorf("failed to send events: %v", err)
|
||||
}
|
||||
|
||||
if err := rsAPI.PerformPublish(ctx, &api.PerformPublishRequest{
|
||||
RoomID: r.ID,
|
||||
Visibility: spec.Public,
|
||||
}, &api.PerformPublishResponse{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return r.ID
|
||||
},
|
||||
wantNewRoom: true,
|
||||
validateFunc: func(t *testing.T, oldRoomID, newRoomID string, rsAPI api.RoomserverInternalAPI) {
|
||||
validate(t, oldRoomID, newRoomID, rsAPI)
|
||||
// check that the new room is published
|
||||
res := &api.QueryPublishedRoomsResponse{}
|
||||
if err := rsAPI.QueryPublishedRooms(ctx, &api.QueryPublishedRoomsRequest{RoomID: newRoomID}, res); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(res.RoomIDs) == 0 {
|
||||
t.Fatalf("expected room to be published, but wasn't: %#v", res.RoomIDs)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with alias",
|
||||
upgradeUser: alice.ID,
|
||||
roomFunc: func(rsAPI api.RoomserverInternalAPI) string {
|
||||
r := test.NewRoom(t, alice)
|
||||
if err := api.SendEvents(ctx, rsAPI, api.KindNew, r.Events(), "test", "test", "test", nil, false); err != nil {
|
||||
t.Errorf("failed to send events: %v", err)
|
||||
}
|
||||
|
||||
if err := rsAPI.SetRoomAlias(ctx, &api.SetRoomAliasRequest{
|
||||
RoomID: r.ID,
|
||||
Alias: "#myroomalias:test",
|
||||
}, &api.SetRoomAliasResponse{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return r.ID
|
||||
},
|
||||
wantNewRoom: true,
|
||||
validateFunc: func(t *testing.T, oldRoomID, newRoomID string, rsAPI api.RoomserverInternalAPI) {
|
||||
validate(t, oldRoomID, newRoomID, rsAPI)
|
||||
// check that the old room has no aliases
|
||||
res := &api.GetAliasesForRoomIDResponse{}
|
||||
if err := rsAPI.GetAliasesForRoomID(ctx, &api.GetAliasesForRoomIDRequest{RoomID: oldRoomID}, res); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(res.Aliases) != 0 {
|
||||
t.Fatalf("expected old room aliases to be empty, but wasn't: %#v", res.Aliases)
|
||||
}
|
||||
|
||||
// check that the new room has aliases
|
||||
if err := rsAPI.GetAliasesForRoomID(ctx, &api.GetAliasesForRoomIDRequest{RoomID: newRoomID}, res); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(res.Aliases) == 0 {
|
||||
t.Fatalf("expected room aliases to be transferred, but wasn't: %#v", res.Aliases)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invites/bans are transferred",
|
||||
upgradeUser: alice.ID,
|
||||
roomFunc: func(rsAPI api.RoomserverInternalAPI) string {
|
||||
r := test.NewRoom(t, alice)
|
||||
r.CreateAndInsert(t, alice, spec.MRoomMember, map[string]interface{}{
|
||||
"membership": spec.Invite,
|
||||
}, test.WithStateKey(bob.ID))
|
||||
r.CreateAndInsert(t, alice, spec.MRoomMember, map[string]interface{}{
|
||||
"membership": spec.Ban,
|
||||
}, test.WithStateKey(charlie.ID))
|
||||
if err := api.SendEvents(ctx, rsAPI, api.KindNew, r.Events(), "test", "test", "test", nil, false); err != nil {
|
||||
t.Errorf("failed to send events: %v", err)
|
||||
}
|
||||
return r.ID
|
||||
},
|
||||
wantNewRoom: true,
|
||||
validateFunc: validate,
|
||||
},
|
||||
{
|
||||
name: "custom state is not taken to the new room", // https://github.com/matrix-org/dendrite/issues/2912
|
||||
upgradeUser: charlie.ID,
|
||||
roomFunc: func(rsAPI api.RoomserverInternalAPI) string {
|
||||
r := test.NewRoom(t, alice, test.RoomVersion(gomatrixserverlib.RoomVersionV6))
|
||||
// Bob and Charlie join
|
||||
r.CreateAndInsert(t, bob, spec.MRoomMember, map[string]interface{}{"membership": spec.Join}, test.WithStateKey(bob.ID))
|
||||
r.CreateAndInsert(t, charlie, spec.MRoomMember, map[string]interface{}{"membership": spec.Join}, test.WithStateKey(charlie.ID))
|
||||
|
||||
// make Charlie an admin so the room can be upgraded
|
||||
r.CreateAndInsert(t, alice, spec.MRoomPowerLevels, gomatrixserverlib.PowerLevelContent{
|
||||
Users: map[string]int64{
|
||||
charlie.ID: 100,
|
||||
},
|
||||
}, test.WithStateKey(""))
|
||||
|
||||
// Alice creates a custom event
|
||||
r.CreateAndInsert(t, alice, "m.custom.event", map[string]interface{}{
|
||||
"random": "data",
|
||||
}, test.WithStateKey(alice.ID))
|
||||
r.CreateAndInsert(t, alice, spec.MRoomMember, map[string]interface{}{"membership": spec.Leave}, test.WithStateKey(alice.ID))
|
||||
|
||||
if err := api.SendEvents(ctx, rsAPI, api.KindNew, r.Events(), "test", "test", "test", nil, false); err != nil {
|
||||
t.Errorf("failed to send events: %v", err)
|
||||
}
|
||||
return r.ID
|
||||
},
|
||||
wantNewRoom: true,
|
||||
validateFunc: validate,
|
||||
},
|
||||
}
|
||||
|
||||
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||
cfg, processCtx, close := testrig.CreateConfig(t, dbType)
|
||||
natsInstance := jetstream.NATSInstance{}
|
||||
defer close()
|
||||
|
||||
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
||||
|
||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
rsAPI.SetUserAPI(userAPI)
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if tc.roomFunc == nil {
|
||||
t.Fatalf("missing roomFunc")
|
||||
}
|
||||
if tc.upgradeUser == "" {
|
||||
tc.upgradeUser = alice.ID
|
||||
}
|
||||
roomID := tc.roomFunc(rsAPI)
|
||||
|
||||
upgradeReq := api.PerformRoomUpgradeRequest{
|
||||
RoomID: roomID,
|
||||
UserID: tc.upgradeUser,
|
||||
RoomVersion: version.DefaultRoomVersion(), // always upgrade to the latest version
|
||||
}
|
||||
upgradeRes := api.PerformRoomUpgradeResponse{}
|
||||
|
||||
if err := rsAPI.PerformRoomUpgrade(processCtx.Context(), &upgradeReq, &upgradeRes); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if tc.wantNewRoom && upgradeRes.NewRoomID == "" {
|
||||
t.Fatalf("expected a new room, but the upgrade failed")
|
||||
}
|
||||
if !tc.wantNewRoom && upgradeRes.NewRoomID != "" {
|
||||
t.Fatalf("expected no new room, but the upgrade succeeded")
|
||||
}
|
||||
if tc.validateFunc != nil {
|
||||
tc.validateFunc(t, roomID, upgradeRes.NewRoomID, rsAPI)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue