sliding-sync/tests-e2e/spaces_test.go
2023-10-11 12:23:46 +01:00

244 lines
7.0 KiB
Go

package syncv3_test
import (
"testing"
"time"
"github.com/matrix-org/complement/b"
"github.com/matrix-org/complement/client"
"github.com/matrix-org/sliding-sync/sync3"
"github.com/matrix-org/sliding-sync/testutils/m"
)
// Make this graph:
//
// A D <-- parents
// .--`--. |
// B C E F <-- children
//
// and query:
//
// spaces[A] => B,C
// spaces[D] => E
// spaces[A,B] => B,C,E
func TestSpacesFilter(t *testing.T) {
alice := registerNewUser(t)
parentA := alice.MustCreateRoom(t, map[string]interface{}{
"preset": "public_chat",
"creation_content": map[string]string{
"type": "m.space",
},
})
parentD := alice.MustCreateRoom(t, map[string]interface{}{
"preset": "public_chat",
"creation_content": map[string]string{
"type": "m.space",
},
})
roomB := alice.MustCreateRoom(t, map[string]interface{}{
"preset": "public_chat",
})
roomC := alice.MustCreateRoom(t, map[string]interface{}{
"preset": "public_chat",
})
roomE := alice.MustCreateRoom(t, map[string]interface{}{
"preset": "public_chat",
})
roomF := alice.MustCreateRoom(t, map[string]interface{}{
"preset": "public_chat",
})
t.Logf("A: %s B: %s C: %s D: %s E: %s F: %s", parentA, roomB, roomC, parentD, roomE, roomF)
alice.SendEventSynced(t, parentA, b.Event{
Type: "m.space.child",
StateKey: &roomB,
Content: map[string]interface{}{
"via": []string{"example.com"},
},
})
alice.SendEventSynced(t, parentA, b.Event{
Type: "m.space.child",
StateKey: &roomC,
Content: map[string]interface{}{
"via": []string{"example.com"},
},
})
alice.SendEventSynced(t, parentD, b.Event{
Type: "m.space.child",
StateKey: &roomE,
Content: map[string]interface{}{
"via": []string{"example.com"},
},
})
time.Sleep(100 * time.Millisecond) // let the proxy process this
doSpacesListRequest := func(spaces []string, pos *string, listMatchers ...m.ListMatcher) *sync3.Response {
t.Helper()
var opts []client.RequestOpt
if pos != nil {
opts = append(opts, WithPos(*pos))
}
t.Logf("requesting rooms in spaces %v", spaces)
res := alice.SlidingSync(t, sync3.Request{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: [][2]int64{{0, 20}},
Filters: &sync3.RequestFilters{
Spaces: spaces,
},
},
},
}, opts...)
m.MatchResponse(t, res, m.MatchList("a", listMatchers...))
return res
}
doInitialSpacesListRequest := func(spaces, wantRoomIDs []string) *sync3.Response {
t.Helper()
t.Logf("requesting initial rooms in spaces %v expecting %v", spaces, wantRoomIDs)
return doSpacesListRequest(spaces, nil, m.MatchV3Count(len(wantRoomIDs)), m.MatchV3Ops(
m.MatchV3SyncOp(
0, int64(len(wantRoomIDs))-1, wantRoomIDs, true,
),
))
}
// spaces[A] => B,C
// spaces[D] => E
// spaces[A,B] => B,C,E
testCases := []struct {
Spaces []string
WantRoomIDs []string
}{
{Spaces: []string{parentA}, WantRoomIDs: []string{roomB, roomC}},
{Spaces: []string{parentD}, WantRoomIDs: []string{roomE}},
{Spaces: []string{parentA, parentD}, WantRoomIDs: []string{roomB, roomC, roomE}},
}
for _, tc := range testCases {
doInitialSpacesListRequest(tc.Spaces, tc.WantRoomIDs)
}
// now move F into D and re-query D
alice.SendEventSynced(t, parentD, b.Event{
Type: "m.space.child",
StateKey: &roomF,
Content: map[string]interface{}{
"via": []string{"example.com"},
},
})
time.Sleep(100 * time.Millisecond) // let the proxy process this
doInitialSpacesListRequest([]string{parentD}, []string{roomF, roomE})
// now remove B and re-query A
alice.SendEventSynced(t, parentA, b.Event{
Type: "m.space.child",
StateKey: &roomB,
Content: map[string]interface{}{},
})
time.Sleep(100 * time.Millisecond) // let the proxy process this
res := doInitialSpacesListRequest([]string{parentA}, []string{roomC})
// now live stream an update to ensure it gets added
alice.SendEventSynced(t, parentA, b.Event{
Type: "m.space.child",
StateKey: &roomB,
Content: map[string]interface{}{
"via": []string{"example.com"},
},
})
time.Sleep(100 * time.Millisecond) // let the proxy process this
res = doSpacesListRequest([]string{parentA}, &res.Pos,
m.MatchV3Count(2), m.MatchV3Ops(
m.MatchV3DeleteOp(1),
m.MatchV3InsertOp(1, roomB),
),
)
// now completely change the space filter and ensure we see the right rooms
doSpacesListRequest([]string{parentD}, &res.Pos,
m.MatchV3Count(2), m.MatchV3Ops(
m.MatchV3InvalidateOp(0, 1),
m.MatchV3SyncOp(0, 1, []string{roomF, roomE}, true),
),
)
}
// Regression test for https://github.com/matrix-org/sliding-sync/issues/81 which has a list
// for invites EXCLUDING spaces, and yet space invites went into this list.
func TestSpacesFilterInvite(t *testing.T) {
alice := registerNewUser(t)
bob := registerNewUser(t)
spaceRoomID := alice.MustCreateRoom(t, map[string]interface{}{
"preset": "public_chat",
"name": "Space Room",
"creation_content": map[string]string{
"type": "m.space",
},
})
normalRoomID := alice.MustCreateRoom(t, map[string]interface{}{
"preset": "public_chat",
"name": "Normal Room",
})
t.Logf("Created space %v normal %v", spaceRoomID, normalRoomID)
alice.InviteRoom(t, spaceRoomID, bob.UserID)
alice.InviteRoom(t, normalRoomID, bob.UserID)
// bob request invites for non-space rooms
res := bob.SlidingSync(t, sync3.Request{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{{0, 20}},
Filters: &sync3.RequestFilters{
IsInvite: &boolTrue,
NotRoomTypes: []*string{ptr("m.space")},
},
RoomSubscription: sync3.RoomSubscription{
RequiredState: [][2]string{{"m.room.name", ""}},
},
},
},
})
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(1), m.MatchV3Ops(
m.MatchV3SyncOp(0, 0, []string{normalRoomID}),
)))
}
// Regression test to catch https://github.com/matrix-org/sliding-sync/issues/85
func TestAddingUnknownChildToSpace(t *testing.T) {
alice := registerNewUser(t)
bob := registerNewUser(t)
t.Log("Alice creates a space and invites Bob.")
parentID := alice.MustCreateRoom(t, map[string]interface{}{
"type": "m.space",
"invite": []string{bob.UserID},
})
t.Log("Bob accepts the invite.")
bob.JoinRoom(t, parentID, nil)
t.Log("Bob requests a new sliding sync.")
res := bob.SlidingSync(t, sync3.Request{
Lists: map[string]sync3.RequestList{
"bob_list": {
RoomSubscription: sync3.RoomSubscription{
TimelineLimit: 10,
},
Ranges: sync3.SliceRanges{{0, 10}},
},
},
})
t.Log("Alice creates a room and marks it as a child of the space.")
childID := alice.MustCreateRoom(t, map[string]interface{}{"preset": "public_chat"})
childEventID := alice.Unsafe_SendEventUnsynced(t, parentID, b.Event{
Type: "m.space.child",
StateKey: ptr(childID),
Content: map[string]interface{}{
"via": []string{"localhost"},
},
})
t.Log("Bob syncs until he sees the m.space.child event in the space.")
// Before the fix, this would panic inside getInitialRoomData, resulting in a 500
res = bob.SlidingSyncUntilEventID(t, res.Pos, parentID, childEventID)
}