2022-07-26 12:24:05 +01:00
|
|
|
package syncv3_test
|
|
|
|
|
|
|
|
import (
|
2023-09-08 14:01:22 +02:00
|
|
|
"fmt"
|
2022-07-26 12:24:05 +01:00
|
|
|
"testing"
|
2022-08-16 11:04:23 +01:00
|
|
|
"time"
|
2022-07-26 12:24:05 +01:00
|
|
|
|
2023-10-11 12:23:46 +01:00
|
|
|
"github.com/matrix-org/complement/b"
|
|
|
|
"github.com/matrix-org/complement/client"
|
2022-12-15 11:08:50 +00:00
|
|
|
"github.com/matrix-org/sliding-sync/sync3"
|
|
|
|
"github.com/matrix-org/sliding-sync/testutils/m"
|
2023-09-19 14:43:41 +02:00
|
|
|
"github.com/tidwall/gjson"
|
2022-07-26 12:24:05 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestRoomStateTransitions(t *testing.T) {
|
|
|
|
alice := registerNewUser(t)
|
|
|
|
bob := registerNewUser(t)
|
|
|
|
|
|
|
|
// make 4 rooms and set bob's membership state in each to a different value.
|
2023-10-11 12:23:46 +01:00
|
|
|
joinRoomID := alice.MustCreateRoom(t, map[string]interface{}{"preset": "public_chat"})
|
2022-07-26 12:24:05 +01:00
|
|
|
bob.JoinRoom(t, joinRoomID, nil)
|
2023-10-11 12:23:46 +01:00
|
|
|
kickRoomID := alice.MustCreateRoom(t, map[string]interface{}{"preset": "public_chat"})
|
2022-07-26 12:24:05 +01:00
|
|
|
bob.JoinRoom(t, kickRoomID, nil)
|
2023-10-11 12:23:46 +01:00
|
|
|
alice.MustDo(t, "POST", []string{"_matrix", "client", "v3", "rooms", kickRoomID, "kick"}, client.WithJSONBody(t, map[string]interface{}{
|
2022-07-26 12:24:05 +01:00
|
|
|
"user_id": bob.UserID,
|
|
|
|
}))
|
2023-10-11 12:23:46 +01:00
|
|
|
banRoomID := alice.MustCreateRoom(t, map[string]interface{}{"preset": "public_chat"})
|
2022-07-26 12:24:05 +01:00
|
|
|
bob.JoinRoom(t, banRoomID, nil)
|
2023-10-11 12:23:46 +01:00
|
|
|
alice.MustDo(t, "POST", []string{"_matrix", "client", "v3", "rooms", banRoomID, "ban"}, client.WithJSONBody(t, map[string]interface{}{
|
2022-07-26 12:24:05 +01:00
|
|
|
"user_id": bob.UserID,
|
|
|
|
}))
|
2023-10-11 12:23:46 +01:00
|
|
|
inviteRoomID := alice.MustCreateRoom(t, map[string]interface{}{"preset": "public_chat"})
|
2022-07-26 12:24:05 +01:00
|
|
|
alice.InviteRoom(t, inviteRoomID, bob.UserID)
|
|
|
|
|
|
|
|
// seed the proxy with Alice data
|
|
|
|
alice.SlidingSync(t, sync3.Request{
|
2022-12-20 13:32:39 +00:00
|
|
|
Lists: map[string]sync3.RequestList{
|
|
|
|
"a": {
|
2022-07-26 12:24:05 +01:00
|
|
|
Ranges: sync3.SliceRanges{
|
|
|
|
{0, 100},
|
|
|
|
},
|
|
|
|
Sort: []string{sync3.SortByRecency},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
|
|
|
|
// bob should see the invited/joined rooms
|
|
|
|
bobRes := bob.SlidingSync(t, sync3.Request{
|
2022-12-20 13:32:39 +00:00
|
|
|
Lists: map[string]sync3.RequestList{
|
|
|
|
"a": {
|
2022-07-26 12:24:05 +01:00
|
|
|
Ranges: sync3.SliceRanges{
|
|
|
|
{0, 100},
|
|
|
|
},
|
|
|
|
Sort: []string{sync3.SortByRecency},
|
|
|
|
RoomSubscription: sync3.RoomSubscription{
|
|
|
|
TimelineLimit: 1,
|
|
|
|
RequiredState: [][2]string{
|
|
|
|
{"m.room.create", ""},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
2022-12-20 13:32:39 +00:00
|
|
|
m.MatchResponse(t, bobRes, m.MatchList("a", m.MatchV3Count(2), m.MatchV3Ops(
|
2023-04-04 23:10:39 +01:00
|
|
|
m.MatchV3SyncOp(0, 1, []string{inviteRoomID, joinRoomID}),
|
2022-07-26 12:24:05 +01:00
|
|
|
)), m.MatchRoomSubscriptions(map[string][]m.RoomMatcher{
|
|
|
|
inviteRoomID: {
|
|
|
|
m.MatchRoomHighlightCount(1),
|
|
|
|
m.MatchRoomInitial(true),
|
|
|
|
m.MatchRoomRequiredState(nil),
|
2023-07-07 15:16:59 +01:00
|
|
|
m.MatchInviteCount(1),
|
|
|
|
m.MatchJoinCount(1),
|
|
|
|
MatchRoomInviteState([]Event{
|
|
|
|
{
|
|
|
|
Type: "m.room.create",
|
|
|
|
StateKey: ptr(""),
|
|
|
|
// no content as it includes the room version which we don't want to guess/hardcode
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: "m.room.join_rules",
|
|
|
|
StateKey: ptr(""),
|
|
|
|
Content: map[string]interface{}{
|
|
|
|
"join_rule": "public",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}, true),
|
2022-07-26 12:24:05 +01:00
|
|
|
},
|
|
|
|
joinRoomID: {},
|
|
|
|
}),
|
|
|
|
)
|
|
|
|
|
|
|
|
// now bob accepts the invite
|
|
|
|
bob.JoinRoom(t, inviteRoomID, nil)
|
|
|
|
|
2022-12-14 18:53:55 +00:00
|
|
|
// wait until the proxy has got this data
|
|
|
|
alice.SlidingSyncUntilMembership(t, "", inviteRoomID, bob, "join")
|
|
|
|
|
2022-07-26 12:24:05 +01:00
|
|
|
// the room should be updated with the initial flag set to replace what was in the invite state
|
|
|
|
bobRes = bob.SlidingSync(t, sync3.Request{
|
2022-12-20 13:32:39 +00:00
|
|
|
Lists: map[string]sync3.RequestList{
|
|
|
|
"a": {
|
2022-07-26 12:24:05 +01:00
|
|
|
Ranges: sync3.SliceRanges{
|
|
|
|
{0, 100},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2022-07-26 17:54:58 +01:00
|
|
|
}, WithPos(bobRes.Pos))
|
2022-12-20 13:32:39 +00:00
|
|
|
m.MatchResponse(t, bobRes, m.MatchNoV3Ops(), m.MatchList("a", m.MatchV3Count(2)), m.MatchRoomSubscription(inviteRoomID,
|
2023-05-23 17:29:14 +01:00
|
|
|
MatchRoomRequiredStateStrict([]Event{
|
2022-07-26 12:24:05 +01:00
|
|
|
{
|
|
|
|
Type: "m.room.create",
|
|
|
|
StateKey: ptr(""),
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
MatchRoomTimelineMostRecent(1, []Event{
|
|
|
|
{
|
|
|
|
Type: "m.room.member",
|
|
|
|
StateKey: ptr(bob.UserID),
|
|
|
|
Content: map[string]interface{}{
|
|
|
|
"membership": "join",
|
|
|
|
"displayname": bob.Localpart,
|
|
|
|
},
|
|
|
|
Sender: bob.UserID,
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
m.MatchRoomInitial(true),
|
2023-07-07 15:16:59 +01:00
|
|
|
m.MatchJoinCount(2),
|
|
|
|
m.MatchInviteCount(0),
|
2022-12-14 18:53:55 +00:00
|
|
|
m.MatchRoomHighlightCount(0),
|
|
|
|
))
|
2022-08-15 18:40:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestInviteRejection(t *testing.T) {
|
|
|
|
alice := registerNewUser(t)
|
|
|
|
bob := registerNewUser(t)
|
|
|
|
|
|
|
|
// ensure that invite state correctly propagates. One room will already be in 'invite' state
|
|
|
|
// prior to the first proxy sync, whereas the 2nd will transition.
|
2023-10-11 12:23:46 +01:00
|
|
|
firstInviteRoomID := alice.MustCreateRoom(t, map[string]interface{}{"preset": "private_chat", "name": "First"})
|
2022-08-15 18:40:43 +01:00
|
|
|
alice.InviteRoom(t, firstInviteRoomID, bob.UserID)
|
2023-10-11 12:23:46 +01:00
|
|
|
secondInviteRoomID := alice.MustCreateRoom(t, map[string]interface{}{"preset": "private_chat", "name": "Second"})
|
2022-08-26 13:54:44 +01:00
|
|
|
t.Logf("TestInviteRejection first %s second %s", firstInviteRoomID, secondInviteRoomID)
|
2022-07-26 12:24:05 +01:00
|
|
|
|
2022-08-15 18:40:43 +01:00
|
|
|
// sync as bob, we should see 1 invite
|
|
|
|
res := bob.SlidingSync(t, sync3.Request{
|
2022-12-20 13:32:39 +00:00
|
|
|
Lists: map[string]sync3.RequestList{
|
|
|
|
"a": {
|
2022-08-15 18:40:43 +01:00
|
|
|
Ranges: sync3.SliceRanges{{0, 20}},
|
|
|
|
Filters: &sync3.RequestFilters{
|
|
|
|
IsInvite: &boolTrue,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
2022-12-20 13:32:39 +00:00
|
|
|
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(1), m.MatchV3Ops(
|
2023-04-05 01:07:07 +01:00
|
|
|
m.MatchV3SyncOp(0, 0, []string{firstInviteRoomID}),
|
2022-08-15 18:40:43 +01:00
|
|
|
)), m.MatchRoomSubscriptionsStrict(map[string][]m.RoomMatcher{
|
|
|
|
firstInviteRoomID: {
|
|
|
|
m.MatchRoomInitial(true),
|
2022-08-30 17:27:58 +01:00
|
|
|
m.MatchInviteCount(1),
|
|
|
|
m.MatchJoinCount(1),
|
2022-08-15 18:40:43 +01:00
|
|
|
MatchRoomInviteState([]Event{
|
|
|
|
{
|
|
|
|
Type: "m.room.name",
|
|
|
|
StateKey: ptr(""),
|
|
|
|
Content: map[string]interface{}{
|
|
|
|
"name": "First",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}, true),
|
|
|
|
},
|
|
|
|
}))
|
|
|
|
|
2023-10-11 12:23:46 +01:00
|
|
|
_, since := bob.MustSync(t, client.SyncReq{})
|
2022-08-15 18:40:43 +01:00
|
|
|
// now invite bob
|
|
|
|
alice.InviteRoom(t, secondInviteRoomID, bob.UserID)
|
2023-10-11 12:23:46 +01:00
|
|
|
since = bob.MustSyncUntil(t, client.SyncReq{Since: since}, client.SyncInvitedTo(bob.UserID, secondInviteRoomID))
|
2022-08-15 18:40:43 +01:00
|
|
|
|
|
|
|
res = bob.SlidingSync(t, sync3.Request{
|
2022-12-20 13:32:39 +00:00
|
|
|
Lists: map[string]sync3.RequestList{
|
|
|
|
"a": {
|
2022-08-15 18:40:43 +01:00
|
|
|
Ranges: sync3.SliceRanges{{0, 20}},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}, WithPos(res.Pos))
|
2022-12-20 13:32:39 +00:00
|
|
|
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(2), m.MatchV3Ops(
|
2022-08-15 18:40:43 +01:00
|
|
|
m.MatchV3DeleteOp(1),
|
|
|
|
m.MatchV3InsertOp(0, secondInviteRoomID),
|
|
|
|
)), m.MatchRoomSubscriptionsStrict(map[string][]m.RoomMatcher{
|
|
|
|
secondInviteRoomID: {
|
|
|
|
m.MatchRoomInitial(true),
|
|
|
|
MatchRoomInviteState([]Event{
|
|
|
|
{
|
|
|
|
Type: "m.room.name",
|
|
|
|
StateKey: ptr(""),
|
|
|
|
Content: map[string]interface{}{
|
|
|
|
"name": "Second",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}, true),
|
|
|
|
},
|
|
|
|
}))
|
|
|
|
|
|
|
|
// now reject the invites
|
|
|
|
|
|
|
|
bob.LeaveRoom(t, firstInviteRoomID)
|
|
|
|
bob.LeaveRoom(t, secondInviteRoomID)
|
2023-10-11 12:23:46 +01:00
|
|
|
bob.MustSyncUntil(t, client.SyncReq{Since: since}, client.SyncLeftFrom(bob.UserID, secondInviteRoomID))
|
2022-08-26 13:54:44 +01:00
|
|
|
// TODO: proxy needs to have processed this event enough for it to be waiting for us
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
2022-08-15 18:40:43 +01:00
|
|
|
|
|
|
|
// the list should be purged
|
|
|
|
res = bob.SlidingSync(t, sync3.Request{
|
2022-12-20 13:32:39 +00:00
|
|
|
Lists: map[string]sync3.RequestList{
|
|
|
|
"a": {
|
2022-08-15 18:40:43 +01:00
|
|
|
Ranges: sync3.SliceRanges{{0, 20}},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}, WithPos(res.Pos))
|
2022-12-20 13:32:39 +00:00
|
|
|
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(0), m.MatchV3Ops(
|
2022-08-15 18:40:43 +01:00
|
|
|
m.MatchV3DeleteOp(1),
|
|
|
|
m.MatchV3DeleteOp(0),
|
|
|
|
)))
|
|
|
|
|
|
|
|
// fresh sync -> no invites
|
|
|
|
res = bob.SlidingSync(t, sync3.Request{
|
2022-12-20 13:32:39 +00:00
|
|
|
Lists: map[string]sync3.RequestList{
|
|
|
|
"a": {
|
2022-08-15 18:40:43 +01:00
|
|
|
Ranges: sync3.SliceRanges{{0, 20}},
|
|
|
|
Filters: &sync3.RequestFilters{
|
|
|
|
IsInvite: &boolTrue,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
2022-12-20 13:32:39 +00:00
|
|
|
m.MatchResponse(t, res, m.MatchNoV3Ops(), m.MatchRoomSubscriptionsStrict(nil), m.MatchList("a", m.MatchV3Count(0)))
|
2022-07-26 12:24:05 +01:00
|
|
|
}
|
2022-08-16 11:04:23 +01:00
|
|
|
|
|
|
|
func TestInviteAcceptance(t *testing.T) {
|
2023-07-27 18:16:13 +01:00
|
|
|
alice := registerNamedUser(t, "alice")
|
|
|
|
bob := registerNamedUser(t, "bob")
|
2022-08-16 11:04:23 +01:00
|
|
|
|
|
|
|
// ensure that invite state correctly propagates. One room will already be in 'invite' state
|
|
|
|
// prior to the first proxy sync, whereas the 2nd will transition.
|
2023-07-27 18:16:13 +01:00
|
|
|
t.Logf("Alice creates two rooms and invites Bob to the first.")
|
2023-10-11 12:23:46 +01:00
|
|
|
firstInviteRoomID := alice.MustCreateRoom(t, map[string]interface{}{"preset": "private_chat", "name": "First"})
|
2022-08-16 11:04:23 +01:00
|
|
|
alice.InviteRoom(t, firstInviteRoomID, bob.UserID)
|
2023-10-11 12:23:46 +01:00
|
|
|
secondInviteRoomID := alice.MustCreateRoom(t, map[string]interface{}{"preset": "private_chat", "name": "Second"})
|
2022-08-16 11:04:23 +01:00
|
|
|
t.Logf("first %s second %s", firstInviteRoomID, secondInviteRoomID)
|
|
|
|
|
2023-07-27 18:16:13 +01:00
|
|
|
t.Log("Sync as Bob, requesting invites only. He should see 1 invite")
|
2022-08-16 11:04:23 +01:00
|
|
|
res := bob.SlidingSync(t, sync3.Request{
|
2022-12-20 13:32:39 +00:00
|
|
|
Lists: map[string]sync3.RequestList{
|
|
|
|
"a": {
|
2022-08-16 11:04:23 +01:00
|
|
|
Ranges: sync3.SliceRanges{{0, 20}},
|
|
|
|
Filters: &sync3.RequestFilters{
|
|
|
|
IsInvite: &boolTrue,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
2022-12-20 13:32:39 +00:00
|
|
|
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(1), m.MatchV3Ops(
|
2023-04-04 23:10:39 +01:00
|
|
|
m.MatchV3SyncOp(0, 0, []string{firstInviteRoomID}),
|
2022-08-16 11:04:23 +01:00
|
|
|
)), m.MatchRoomSubscriptionsStrict(map[string][]m.RoomMatcher{
|
|
|
|
firstInviteRoomID: {
|
|
|
|
m.MatchRoomInitial(true),
|
2022-08-30 17:27:58 +01:00
|
|
|
m.MatchInviteCount(1),
|
|
|
|
m.MatchJoinCount(1),
|
2022-08-16 11:04:23 +01:00
|
|
|
MatchRoomInviteState([]Event{
|
|
|
|
{
|
|
|
|
Type: "m.room.name",
|
|
|
|
StateKey: ptr(""),
|
|
|
|
Content: map[string]interface{}{
|
|
|
|
"name": "First",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}, true),
|
|
|
|
},
|
|
|
|
}))
|
|
|
|
|
2023-07-27 18:16:13 +01:00
|
|
|
t.Log("Alice invites bob to room 2.")
|
2022-08-16 11:04:23 +01:00
|
|
|
alice.InviteRoom(t, secondInviteRoomID, bob.UserID)
|
2023-07-27 18:16:13 +01:00
|
|
|
t.Log("Alice syncs until she sees Bob's invite.")
|
2022-12-14 18:53:55 +00:00
|
|
|
alice.SlidingSyncUntilMembership(t, "", secondInviteRoomID, bob, "invite")
|
2022-08-16 11:04:23 +01:00
|
|
|
|
2023-07-27 18:16:13 +01:00
|
|
|
t.Log("Bob syncs. He should see the invite to room 2 as well.")
|
2022-08-16 11:04:23 +01:00
|
|
|
res = bob.SlidingSync(t, sync3.Request{
|
2022-12-20 13:32:39 +00:00
|
|
|
Lists: map[string]sync3.RequestList{
|
|
|
|
"a": {
|
2022-08-16 11:04:23 +01:00
|
|
|
Ranges: sync3.SliceRanges{{0, 20}},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}, WithPos(res.Pos))
|
2022-12-20 13:32:39 +00:00
|
|
|
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(2), m.MatchV3Ops(
|
2022-08-16 11:04:23 +01:00
|
|
|
m.MatchV3DeleteOp(1),
|
|
|
|
m.MatchV3InsertOp(0, secondInviteRoomID),
|
|
|
|
)), m.MatchRoomSubscriptionsStrict(map[string][]m.RoomMatcher{
|
|
|
|
secondInviteRoomID: {
|
|
|
|
m.MatchRoomInitial(true),
|
2022-08-30 17:27:58 +01:00
|
|
|
m.MatchInviteCount(1),
|
|
|
|
m.MatchJoinCount(1),
|
2022-08-16 11:04:23 +01:00
|
|
|
MatchRoomInviteState([]Event{
|
|
|
|
{
|
|
|
|
Type: "m.room.name",
|
|
|
|
StateKey: ptr(""),
|
|
|
|
Content: map[string]interface{}{
|
|
|
|
"name": "Second",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}, true),
|
|
|
|
},
|
|
|
|
}))
|
|
|
|
|
2023-07-27 18:16:13 +01:00
|
|
|
t.Log("Bob accept the invites.")
|
2022-08-16 11:04:23 +01:00
|
|
|
bob.JoinRoom(t, firstInviteRoomID, nil)
|
|
|
|
bob.JoinRoom(t, secondInviteRoomID, nil)
|
2023-07-27 18:16:13 +01:00
|
|
|
|
|
|
|
t.Log("Alice syncs until she sees Bob join room 1.")
|
2022-12-14 18:53:55 +00:00
|
|
|
alice.SlidingSyncUntilMembership(t, "", firstInviteRoomID, bob, "join")
|
2023-07-27 18:16:13 +01:00
|
|
|
t.Log("Alice syncs until she sees Bob join room 2.")
|
2022-12-14 18:53:55 +00:00
|
|
|
alice.SlidingSyncUntilMembership(t, "", secondInviteRoomID, bob, "join")
|
2022-08-16 11:04:23 +01:00
|
|
|
|
2023-07-27 18:16:13 +01:00
|
|
|
t.Log("Bob does an incremental sync")
|
2022-08-16 11:04:23 +01:00
|
|
|
res = bob.SlidingSync(t, sync3.Request{
|
2022-12-20 13:32:39 +00:00
|
|
|
Lists: map[string]sync3.RequestList{
|
|
|
|
"a": {
|
2022-08-16 11:04:23 +01:00
|
|
|
Ranges: sync3.SliceRanges{{0, 20}},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}, WithPos(res.Pos))
|
2023-07-27 18:16:13 +01:00
|
|
|
t.Log("Both of his invites should be purged.")
|
2022-12-20 13:32:39 +00:00
|
|
|
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(0), m.MatchV3Ops(
|
2022-08-16 11:04:23 +01:00
|
|
|
m.MatchV3DeleteOp(1),
|
|
|
|
m.MatchV3DeleteOp(0),
|
|
|
|
)))
|
|
|
|
|
2023-07-27 18:16:13 +01:00
|
|
|
t.Log("Bob makes a fresh sliding sync request.")
|
2022-08-16 11:04:23 +01:00
|
|
|
res = bob.SlidingSync(t, sync3.Request{
|
2022-12-20 13:32:39 +00:00
|
|
|
Lists: map[string]sync3.RequestList{
|
|
|
|
"a": {
|
2022-08-16 11:04:23 +01:00
|
|
|
Ranges: sync3.SliceRanges{{0, 20}},
|
|
|
|
Filters: &sync3.RequestFilters{
|
|
|
|
IsInvite: &boolTrue,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
2023-07-27 18:16:13 +01:00
|
|
|
t.Log("He should see no invites.")
|
2022-12-20 13:32:39 +00:00
|
|
|
m.MatchResponse(t, res, m.MatchNoV3Ops(), m.MatchRoomSubscriptionsStrict(nil), m.MatchList("a", m.MatchV3Count(0)))
|
2022-08-16 11:04:23 +01:00
|
|
|
}
|
2022-08-30 17:27:58 +01:00
|
|
|
|
2023-04-27 18:46:35 +01:00
|
|
|
// Regression test for https://github.com/matrix-org/sliding-sync/issues/66
|
|
|
|
// whereby the invite fails to appear to clients when you invite->reject->invite
|
|
|
|
func TestInviteRejectionTwice(t *testing.T) {
|
|
|
|
alice := registerNewUser(t)
|
|
|
|
bob := registerNewUser(t)
|
|
|
|
roomName := "It's-a-me-invitio"
|
2023-10-11 12:23:46 +01:00
|
|
|
inviteRoomID := alice.MustCreateRoom(t, map[string]interface{}{"preset": "private_chat", "name": roomName})
|
2023-04-27 18:46:35 +01:00
|
|
|
t.Logf("TestInviteRejectionTwice room %s", inviteRoomID)
|
|
|
|
|
|
|
|
// sync as bob, we see no invites yet.
|
|
|
|
res := bob.SlidingSync(t, sync3.Request{
|
|
|
|
Lists: map[string]sync3.RequestList{
|
|
|
|
"a": {
|
|
|
|
Ranges: sync3.SliceRanges{{0, 20}},
|
|
|
|
Filters: &sync3.RequestFilters{
|
|
|
|
IsInvite: &boolTrue,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(0)), m.MatchRoomSubscriptionsStrict(nil))
|
|
|
|
|
|
|
|
// now invite bob
|
|
|
|
alice.InviteRoom(t, inviteRoomID, bob.UserID)
|
|
|
|
// sync as bob until we see the room (we aren't interested in this invite)
|
|
|
|
res = bob.SlidingSyncUntilMembership(t, res.Pos, inviteRoomID, bob, "invite")
|
|
|
|
t.Logf("bob is invited")
|
|
|
|
// reject the invite and sync until we see it disappear (we aren't interested in this rejection either!)
|
|
|
|
bob.LeaveRoom(t, inviteRoomID)
|
|
|
|
res = bob.SlidingSyncUntilMembership(t, res.Pos, inviteRoomID, bob, "leave")
|
|
|
|
|
|
|
|
// now invite bob again, we should see this (the regression was that we didn't until we initial synced!)
|
|
|
|
alice.InviteRoom(t, inviteRoomID, bob.UserID)
|
|
|
|
bob.SlidingSyncUntil(t, res.Pos, sync3.Request{}, func(r *sync3.Response) error {
|
|
|
|
return m.MatchRoomSubscriptionsStrict(map[string][]m.RoomMatcher{
|
|
|
|
inviteRoomID: {
|
|
|
|
m.LogRoom(t),
|
|
|
|
m.MatchInviteCount(1),
|
|
|
|
MatchRoomInviteState([]Event{
|
|
|
|
{
|
|
|
|
Type: "m.room.name",
|
|
|
|
StateKey: ptr(""),
|
|
|
|
Content: map[string]interface{}{
|
|
|
|
"name": roomName,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}, true),
|
|
|
|
},
|
|
|
|
})(r)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-09-08 12:01:59 +02:00
|
|
|
func TestLeavingRoomReturnsOneEvent(t *testing.T) {
|
|
|
|
alice := registerNewUser(t)
|
|
|
|
bob := registerNewUser(t)
|
|
|
|
roomName := "It's-a-me-invitio"
|
|
|
|
|
2023-09-08 14:01:22 +02:00
|
|
|
for _, aliceSyncing := range []bool{false, true} {
|
|
|
|
t.Run(fmt.Sprintf("leaving a room returns one leave event (multiple poller=%v)", aliceSyncing), func(t *testing.T) {
|
2023-10-11 12:23:46 +01:00
|
|
|
inviteRoomID := alice.MustCreateRoom(t, map[string]interface{}{"preset": "private_chat", "name": roomName})
|
2023-09-08 14:01:22 +02:00
|
|
|
t.Logf("TestLeavingRoomReturnsOneEvent room %s", inviteRoomID)
|
2023-09-08 12:01:59 +02:00
|
|
|
|
2023-09-08 14:01:22 +02:00
|
|
|
if aliceSyncing {
|
|
|
|
alice.SlidingSync(t, sync3.Request{})
|
|
|
|
}
|
2023-09-08 12:01:59 +02:00
|
|
|
|
2023-09-08 14:01:22 +02:00
|
|
|
// sync as bob, we see no invites yet.
|
|
|
|
res := bob.SlidingSync(t, sync3.Request{
|
|
|
|
Lists: map[string]sync3.RequestList{
|
|
|
|
"a": {
|
|
|
|
Ranges: sync3.SliceRanges{{0, 20}},
|
|
|
|
Filters: &sync3.RequestFilters{
|
|
|
|
IsInvite: &boolTrue,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(0)), m.MatchRoomSubscriptionsStrict(nil))
|
|
|
|
|
|
|
|
// now invite bob
|
|
|
|
alice.InviteRoom(t, inviteRoomID, bob.UserID)
|
|
|
|
// sync as bob until we see the room
|
|
|
|
res = bob.SlidingSyncUntilMembership(t, res.Pos, inviteRoomID, bob, "invite")
|
|
|
|
t.Logf("bob is invited")
|
|
|
|
|
|
|
|
// join the room
|
|
|
|
bob.JoinRoom(t, inviteRoomID, []string{})
|
|
|
|
res = bob.SlidingSyncUntilMembership(t, res.Pos, inviteRoomID, bob, "join")
|
|
|
|
t.Logf("bob joined")
|
|
|
|
|
|
|
|
// leave the room again, we should receive exactly one leave response
|
|
|
|
bob.LeaveRoom(t, inviteRoomID)
|
|
|
|
res = bob.SlidingSyncUntilMembership(t, res.Pos, inviteRoomID, bob, "leave")
|
|
|
|
|
2023-09-13 13:04:28 +02:00
|
|
|
if room, ok := res.Rooms[inviteRoomID]; ok {
|
|
|
|
// If alice is NOT syncing, we run into this failure mode
|
|
|
|
if c := len(room.Timeline); c > 1 {
|
|
|
|
for _, ev := range res.Rooms[inviteRoomID].Timeline {
|
|
|
|
t.Logf("[multiple poller=%v] Event: %s", aliceSyncing, ev)
|
|
|
|
}
|
|
|
|
t.Errorf("[multiple poller=%v] expected 1 timeline event, got %d", aliceSyncing, c)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
t.Errorf("[multiple poller=%v] expected room %s in response, but didn't find it", aliceSyncing, inviteRoomID)
|
|
|
|
}
|
|
|
|
|
|
|
|
res = bob.SlidingSync(t, sync3.Request{}, WithPos(res.Pos))
|
|
|
|
|
|
|
|
// this should not happen, as we already send down the leave event
|
2023-09-13 14:53:11 +02:00
|
|
|
// If alice is syncing, we run into this failure mode
|
2023-09-13 13:04:28 +02:00
|
|
|
if room, ok := res.Rooms[inviteRoomID]; ok {
|
|
|
|
for _, ev := range room.Timeline {
|
|
|
|
t.Logf("[multiple poller=%v] Event: %s", aliceSyncing, ev)
|
|
|
|
}
|
|
|
|
t.Errorf("[multiple poller=%v] expected room not to be in response", aliceSyncing)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Basically the same as above, without joining the room (separate test to make sure
|
|
|
|
// we don't already have a poller running for Alice)
|
|
|
|
func TestRejectingInviteReturnsOneEvent(t *testing.T) {
|
|
|
|
alice := registerNewUser(t)
|
|
|
|
bob := registerNewUser(t)
|
|
|
|
roomName := "It's-a-me-invitio"
|
|
|
|
|
|
|
|
for _, aliceSyncing := range []bool{false, true} {
|
|
|
|
t.Run(fmt.Sprintf("rejecting an invite returns one leave event (multiple poller=%v)", aliceSyncing), func(t *testing.T) {
|
2023-10-11 12:23:46 +01:00
|
|
|
inviteRoomID := alice.MustCreateRoom(t, map[string]interface{}{"preset": "private_chat", "name": roomName})
|
2023-09-13 13:04:28 +02:00
|
|
|
t.Logf("TestRejectingInviteReturnsOneEvent room %s", inviteRoomID)
|
|
|
|
|
|
|
|
if aliceSyncing {
|
|
|
|
alice.SlidingSync(t, sync3.Request{})
|
|
|
|
}
|
|
|
|
|
|
|
|
// sync as bob, we see no invites yet.
|
|
|
|
res := bob.SlidingSync(t, sync3.Request{
|
|
|
|
Lists: map[string]sync3.RequestList{
|
|
|
|
"a": {
|
|
|
|
Ranges: sync3.SliceRanges{{0, 20}},
|
|
|
|
Filters: &sync3.RequestFilters{
|
|
|
|
IsInvite: &boolTrue,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(0)), m.MatchRoomSubscriptionsStrict(nil))
|
|
|
|
|
|
|
|
// now invite bob
|
|
|
|
alice.InviteRoom(t, inviteRoomID, bob.UserID)
|
|
|
|
// sync as bob until we see the room
|
|
|
|
res = bob.SlidingSyncUntilMembership(t, res.Pos, inviteRoomID, bob, "invite")
|
|
|
|
t.Logf("bob is invited")
|
|
|
|
|
|
|
|
// reject the invite, we should receive exactly one leave response
|
|
|
|
bob.LeaveRoom(t, inviteRoomID)
|
|
|
|
res = bob.SlidingSyncUntilMembership(t, res.Pos, inviteRoomID, bob, "leave")
|
|
|
|
t.Logf("bob rejected the invite")
|
|
|
|
|
2023-09-08 14:01:22 +02:00
|
|
|
if room, ok := res.Rooms[inviteRoomID]; ok {
|
|
|
|
// If alice is NOT syncing, we run into this failure mode
|
|
|
|
if c := len(room.Timeline); c > 1 {
|
|
|
|
for _, ev := range res.Rooms[inviteRoomID].Timeline {
|
|
|
|
t.Logf("[multiple poller=%v] Event: %s", aliceSyncing, ev)
|
|
|
|
}
|
|
|
|
t.Errorf("[multiple poller=%v] expected 1 timeline event, got %d", aliceSyncing, c)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
t.Errorf("[multiple poller=%v] expected room %s in response, but didn't find it", aliceSyncing, inviteRoomID)
|
2023-09-08 12:01:59 +02:00
|
|
|
}
|
|
|
|
|
2023-09-08 14:01:22 +02:00
|
|
|
res = bob.SlidingSync(t, sync3.Request{}, WithPos(res.Pos))
|
2023-09-08 12:01:59 +02:00
|
|
|
|
2023-09-08 14:01:22 +02:00
|
|
|
// this should not happen, as we already send down the leave event
|
2023-09-13 14:53:11 +02:00
|
|
|
// If alice is syncing, we run into this failure mode
|
2023-09-08 14:01:22 +02:00
|
|
|
if room, ok := res.Rooms[inviteRoomID]; ok {
|
|
|
|
for _, ev := range room.Timeline {
|
|
|
|
t.Logf("[multiple poller=%v] Event: %s", aliceSyncing, ev)
|
|
|
|
}
|
|
|
|
t.Errorf("[multiple poller=%v] expected room not to be in response", aliceSyncing)
|
|
|
|
}
|
|
|
|
})
|
2023-09-08 12:01:59 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-15 15:08:07 +02:00
|
|
|
// Test to check that room heroes are returned if the membership changes
|
|
|
|
func TestHeroesOnMembershipChanges(t *testing.T) {
|
|
|
|
alice := registerNewUser(t)
|
|
|
|
bob := registerNewUser(t)
|
|
|
|
charlie := registerNewUser(t)
|
|
|
|
|
|
|
|
t.Run("nameless room uses heroes to calculate roomname", func(t *testing.T) {
|
|
|
|
// create a room without a name, to ensure we calculate the room name based on
|
|
|
|
// room heroes
|
2023-10-11 12:23:46 +01:00
|
|
|
roomID := alice.MustCreateRoom(t, map[string]interface{}{"preset": "public_chat"})
|
2023-09-15 15:08:07 +02:00
|
|
|
|
|
|
|
bob.JoinRoom(t, roomID, []string{})
|
|
|
|
|
|
|
|
res := alice.SlidingSyncUntilMembership(t, "", roomID, bob, "join")
|
|
|
|
// we expect to see Bob as a hero
|
|
|
|
if c := len(res.Rooms[roomID].Heroes); c > 1 {
|
|
|
|
t.Errorf("expected 1 room hero, got %d", c)
|
|
|
|
}
|
|
|
|
if gotUserID := res.Rooms[roomID].Heroes[0].ID; gotUserID != bob.UserID {
|
|
|
|
t.Errorf("expected userID %q, got %q", gotUserID, bob.UserID)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now join with Charlie, the heroes and the room name should change
|
|
|
|
charlie.JoinRoom(t, roomID, []string{})
|
|
|
|
res = alice.SlidingSyncUntilMembership(t, res.Pos, roomID, charlie, "join")
|
|
|
|
|
|
|
|
// we expect to see Bob as a hero
|
|
|
|
if c := len(res.Rooms[roomID].Heroes); c > 2 {
|
|
|
|
t.Errorf("expected 2 room hero, got %d", c)
|
|
|
|
}
|
|
|
|
if gotUserID := res.Rooms[roomID].Heroes[0].ID; gotUserID != bob.UserID {
|
|
|
|
t.Errorf("expected userID %q, got %q", gotUserID, bob.UserID)
|
|
|
|
}
|
|
|
|
if gotUserID := res.Rooms[roomID].Heroes[1].ID; gotUserID != charlie.UserID {
|
|
|
|
t.Errorf("expected userID %q, got %q", gotUserID, charlie.UserID)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Send a message, the heroes shouldn't change
|
2023-10-11 12:23:46 +01:00
|
|
|
msgEv := bob.SendEventSynced(t, roomID, b.Event{
|
2023-09-15 15:08:07 +02:00
|
|
|
Type: "m.room.roomID",
|
|
|
|
Content: map[string]interface{}{"body": "Hello world", "msgtype": "m.text"},
|
|
|
|
})
|
|
|
|
|
|
|
|
res = alice.SlidingSyncUntilEventID(t, res.Pos, roomID, msgEv)
|
|
|
|
if len(res.Rooms[roomID].Heroes) > 0 {
|
|
|
|
t.Errorf("expected no change to room heros")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now leave with Charlie, only Bob should be in the heroes list
|
|
|
|
charlie.LeaveRoom(t, roomID)
|
|
|
|
res = alice.SlidingSyncUntilMembership(t, res.Pos, roomID, charlie, "leave")
|
|
|
|
if c := len(res.Rooms[roomID].Heroes); c > 1 {
|
|
|
|
t.Errorf("expected 1 room hero, got %d", c)
|
|
|
|
}
|
|
|
|
if gotUserID := res.Rooms[roomID].Heroes[0].ID; gotUserID != bob.UserID {
|
|
|
|
t.Errorf("expected userID %q, got %q", gotUserID, bob.UserID)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("named rooms don't have heroes", func(t *testing.T) {
|
2023-10-11 12:23:46 +01:00
|
|
|
namedRoomID := alice.MustCreateRoom(t, map[string]interface{}{"preset": "public_chat", "name": "my room without heroes"})
|
2023-09-15 15:08:07 +02:00
|
|
|
// this makes sure that even if bob is joined, we don't return any heroes
|
|
|
|
bob.JoinRoom(t, namedRoomID, []string{})
|
|
|
|
|
|
|
|
res := alice.SlidingSyncUntilMembership(t, "", namedRoomID, bob, "join")
|
|
|
|
if len(res.Rooms[namedRoomID].Heroes) > 0 {
|
|
|
|
t.Errorf("expected no heroes, got %#v", res.Rooms[namedRoomID].Heroes)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("rooms with aliases don't have heroes", func(t *testing.T) {
|
2023-10-11 12:23:46 +01:00
|
|
|
aliasRoomID := alice.MustCreateRoom(t, map[string]interface{}{"preset": "public_chat"})
|
2023-09-15 15:08:07 +02:00
|
|
|
|
|
|
|
alias := fmt.Sprintf("#%s-%d:%s", t.Name(), time.Now().Unix(), alice.Domain)
|
2023-10-11 12:23:46 +01:00
|
|
|
alice.MustDo(t, "PUT", []string{"_matrix", "client", "v3", "directory", "room", alias},
|
|
|
|
client.WithJSONBody(t, map[string]any{"room_id": aliasRoomID}),
|
2023-09-15 15:08:07 +02:00
|
|
|
)
|
2023-10-11 12:23:46 +01:00
|
|
|
alice.Unsafe_SendEventUnsynced(t, aliasRoomID, b.Event{
|
|
|
|
Type: "m.room.canonical_alias",
|
|
|
|
StateKey: ptr(""),
|
|
|
|
Content: map[string]any{
|
|
|
|
"alias": alias,
|
|
|
|
},
|
2023-09-15 15:08:07 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
bob.JoinRoom(t, aliasRoomID, []string{})
|
|
|
|
|
|
|
|
res := alice.SlidingSyncUntilMembership(t, "", aliasRoomID, bob, "join")
|
|
|
|
if len(res.Rooms[aliasRoomID].Heroes) > 0 {
|
|
|
|
t.Errorf("expected no heroes, got %#v", res.Rooms[aliasRoomID].Heroes)
|
|
|
|
}
|
|
|
|
})
|
2023-09-15 16:57:12 +02:00
|
|
|
|
|
|
|
t.Run("can set heroes=true on room subscriptions", func(t *testing.T) {
|
2023-10-11 12:23:46 +01:00
|
|
|
subRoomID := alice.MustCreateRoom(t, map[string]interface{}{"preset": "public_chat"})
|
2023-09-15 16:57:12 +02:00
|
|
|
bob.JoinRoom(t, subRoomID, []string{})
|
|
|
|
|
2023-09-19 14:43:41 +02:00
|
|
|
res := alice.SlidingSyncUntilMembership(t, "", subRoomID, bob, "join")
|
2023-09-15 16:57:12 +02:00
|
|
|
if c := len(res.Rooms[subRoomID].Heroes); c > 1 {
|
|
|
|
t.Errorf("expected 1 room hero, got %d", c)
|
|
|
|
}
|
|
|
|
if gotUserID := res.Rooms[subRoomID].Heroes[0].ID; gotUserID != bob.UserID {
|
|
|
|
t.Errorf("expected userID %q, got %q", gotUserID, bob.UserID)
|
|
|
|
}
|
|
|
|
})
|
2023-09-19 08:28:06 +02:00
|
|
|
|
|
|
|
t.Run("can set heroes=true in lists", func(t *testing.T) {
|
2023-10-11 12:23:46 +01:00
|
|
|
listRoomID := alice.MustCreateRoom(t, map[string]interface{}{"preset": "public_chat"})
|
2023-09-19 08:28:06 +02:00
|
|
|
bob.JoinRoom(t, listRoomID, []string{})
|
|
|
|
|
|
|
|
res := alice.SlidingSyncUntil(t, "", sync3.Request{
|
|
|
|
Lists: map[string]sync3.RequestList{
|
|
|
|
"all_rooms": {
|
2023-09-19 09:37:58 +02:00
|
|
|
Ranges: sync3.SliceRanges{{0, 20}},
|
2023-09-19 08:28:06 +02:00
|
|
|
RoomSubscription: sync3.RoomSubscription{
|
|
|
|
Heroes: &boolTrue,
|
|
|
|
TimelineLimit: 10,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}, func(response *sync3.Response) error {
|
|
|
|
r, ok := response.Rooms[listRoomID]
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("room %q not in response", listRoomID)
|
|
|
|
}
|
|
|
|
// wait for bob to be joined
|
|
|
|
for _, ev := range r.Timeline {
|
|
|
|
if gjson.GetBytes(ev, "type").Str != "m.room.member" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if gjson.GetBytes(ev, "state_key").Str != bob.UserID {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if gjson.GetBytes(ev, "content.membership").Str == "join" {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return fmt.Errorf("%s is not joined to room %q", bob.UserID, listRoomID)
|
|
|
|
})
|
|
|
|
if c := len(res.Rooms[listRoomID].Heroes); c > 1 {
|
|
|
|
t.Errorf("expected 1 room hero, got %d", c)
|
|
|
|
}
|
|
|
|
if gotUserID := res.Rooms[listRoomID].Heroes[0].ID; gotUserID != bob.UserID {
|
|
|
|
t.Errorf("expected userID %q, got %q", gotUserID, bob.UserID)
|
|
|
|
}
|
|
|
|
})
|
2023-09-15 15:08:07 +02:00
|
|
|
}
|
|
|
|
|
2022-08-30 17:27:58 +01:00
|
|
|
// test invite/join counts update and are accurate
|
|
|
|
func TestMemberCounts(t *testing.T) {
|
|
|
|
alice := registerNewUser(t)
|
|
|
|
bob := registerNewUser(t)
|
|
|
|
charlie := registerNewUser(t)
|
|
|
|
|
2023-10-11 12:23:46 +01:00
|
|
|
firstRoomID := alice.MustCreateRoom(t, map[string]interface{}{"preset": "public_chat", "name": "First"})
|
2022-08-30 17:27:58 +01:00
|
|
|
alice.InviteRoom(t, firstRoomID, bob.UserID)
|
2023-10-11 12:23:46 +01:00
|
|
|
secondRoomID := alice.MustCreateRoom(t, map[string]interface{}{"preset": "public_chat", "name": "Second"})
|
2022-08-30 17:27:58 +01:00
|
|
|
alice.InviteRoom(t, secondRoomID, bob.UserID)
|
|
|
|
charlie.JoinRoom(t, secondRoomID, nil)
|
|
|
|
t.Logf("first %s second %s", firstRoomID, secondRoomID)
|
|
|
|
|
|
|
|
// sync as bob, we should see 2 invited rooms with the same join counts so as not to leak join counts
|
|
|
|
res := bob.SlidingSync(t, sync3.Request{
|
2022-12-20 13:32:39 +00:00
|
|
|
Lists: map[string]sync3.RequestList{
|
|
|
|
"a": {
|
2022-08-30 17:27:58 +01:00
|
|
|
Ranges: sync3.SliceRanges{{0, 20}},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
2022-12-20 13:32:39 +00:00
|
|
|
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(2), m.MatchV3Ops(
|
2023-04-04 23:10:39 +01:00
|
|
|
m.MatchV3SyncOp(0, 1, []string{firstRoomID, secondRoomID}, true),
|
2022-08-30 17:27:58 +01:00
|
|
|
)), m.MatchRoomSubscriptionsStrict(map[string][]m.RoomMatcher{
|
|
|
|
firstRoomID: {
|
|
|
|
m.MatchRoomInitial(true),
|
|
|
|
m.MatchInviteCount(1),
|
|
|
|
m.MatchJoinCount(1),
|
|
|
|
MatchRoomInviteState([]Event{
|
|
|
|
{
|
|
|
|
Type: "m.room.name",
|
|
|
|
StateKey: ptr(""),
|
|
|
|
Content: map[string]interface{}{
|
|
|
|
"name": "First",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}, true),
|
|
|
|
},
|
|
|
|
secondRoomID: {
|
|
|
|
m.MatchRoomInitial(true),
|
|
|
|
m.MatchInviteCount(1),
|
|
|
|
m.MatchJoinCount(1),
|
|
|
|
MatchRoomInviteState([]Event{
|
|
|
|
{
|
|
|
|
Type: "m.room.name",
|
|
|
|
StateKey: ptr(""),
|
|
|
|
Content: map[string]interface{}{
|
|
|
|
"name": "Second",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}, true),
|
|
|
|
},
|
|
|
|
}))
|
|
|
|
|
|
|
|
// join both rooms, the counts should now reflect reality
|
|
|
|
bob.JoinRoom(t, firstRoomID, nil)
|
|
|
|
bob.JoinRoom(t, secondRoomID, nil)
|
2022-12-14 18:53:55 +00:00
|
|
|
alice.SlidingSyncUntilMembership(t, "", firstRoomID, bob, "join")
|
|
|
|
alice.SlidingSyncUntilMembership(t, "", secondRoomID, bob, "join")
|
2022-08-30 17:27:58 +01:00
|
|
|
|
|
|
|
res = bob.SlidingSync(t, sync3.Request{
|
2022-12-20 13:32:39 +00:00
|
|
|
Lists: map[string]sync3.RequestList{
|
|
|
|
"a": {
|
2022-08-30 17:27:58 +01:00
|
|
|
Ranges: sync3.SliceRanges{{0, 20}},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}, WithPos(res.Pos))
|
|
|
|
m.MatchResponse(t, res, m.MatchRoomSubscriptionsStrict(map[string][]m.RoomMatcher{
|
|
|
|
firstRoomID: {
|
|
|
|
m.MatchRoomInitial(true),
|
|
|
|
m.MatchInviteCount(0),
|
|
|
|
m.MatchJoinCount(2),
|
|
|
|
},
|
|
|
|
secondRoomID: {
|
|
|
|
m.MatchRoomInitial(true),
|
|
|
|
m.MatchInviteCount(0),
|
|
|
|
m.MatchJoinCount(3),
|
|
|
|
},
|
|
|
|
}))
|
|
|
|
|
|
|
|
// sending a message shouldn't update the count as it wastes bandwidth
|
2023-10-11 12:23:46 +01:00
|
|
|
charlie.SendEventSynced(t, secondRoomID, b.Event{
|
2022-08-30 17:27:58 +01:00
|
|
|
Type: "m.room.message",
|
|
|
|
Content: map[string]interface{}{"body": "ping", "msgtype": "m.text"},
|
|
|
|
})
|
|
|
|
res = bob.SlidingSync(t, sync3.Request{
|
2022-12-20 13:32:39 +00:00
|
|
|
Lists: map[string]sync3.RequestList{
|
|
|
|
"a": {
|
2022-08-30 17:27:58 +01:00
|
|
|
Ranges: sync3.SliceRanges{{0, 20}},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}, WithPos(res.Pos))
|
|
|
|
m.MatchResponse(t, res, m.MatchRoomSubscriptionsStrict(map[string][]m.RoomMatcher{
|
|
|
|
secondRoomID: {
|
|
|
|
m.MatchRoomInitial(false),
|
2023-07-07 15:16:59 +01:00
|
|
|
m.MatchNoInviteCount(),
|
2022-08-30 17:27:58 +01:00
|
|
|
m.MatchJoinCount(0), // omitempty
|
|
|
|
},
|
|
|
|
}))
|
|
|
|
|
|
|
|
// leaving a room should update the count
|
|
|
|
charlie.LeaveRoom(t, secondRoomID)
|
2023-10-11 12:23:46 +01:00
|
|
|
bob.MustSyncUntil(t, client.SyncReq{}, client.SyncLeftFrom(charlie.UserID, secondRoomID))
|
2022-08-30 17:27:58 +01:00
|
|
|
|
|
|
|
res = bob.SlidingSync(t, sync3.Request{
|
2022-12-20 13:32:39 +00:00
|
|
|
Lists: map[string]sync3.RequestList{
|
|
|
|
"a": {
|
2022-08-30 17:27:58 +01:00
|
|
|
Ranges: sync3.SliceRanges{{0, 20}},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}, WithPos(res.Pos))
|
|
|
|
m.MatchResponse(t, res, m.MatchRoomSubscriptionsStrict(map[string][]m.RoomMatcher{
|
|
|
|
secondRoomID: {
|
|
|
|
m.MatchRoomInitial(false),
|
2023-07-07 15:16:59 +01:00
|
|
|
m.MatchNoInviteCount(),
|
2022-08-30 17:27:58 +01:00
|
|
|
m.MatchJoinCount(2),
|
|
|
|
},
|
|
|
|
}))
|
|
|
|
}
|
2023-10-17 17:43:21 +01:00
|
|
|
|
|
|
|
func TestPreemptiveBanIsNotLeaked(t *testing.T) {
|
|
|
|
alice := registerNamedUser(t, "alice")
|
|
|
|
nigel := registerNamedUser(t, "nigel")
|
|
|
|
|
|
|
|
t.Log("Alice creates a public room and a DM with Nigel.")
|
|
|
|
public := alice.MustCreateRoom(t, map[string]interface{}{"preset": "public_chat"})
|
|
|
|
dm := alice.MustCreateRoom(t, map[string]interface{}{"preset": "private_chat", "invite": []string{nigel.UserID}})
|
|
|
|
|
|
|
|
t.Log("Nigel joins the DM")
|
|
|
|
nigel.JoinRoom(t, dm, nil)
|
|
|
|
|
|
|
|
t.Log("Alice sends a sentinel message into the DM.")
|
|
|
|
dmSentinel := alice.SendEventSynced(t, dm, b.Event{
|
|
|
|
Type: "m.room.message",
|
|
|
|
Content: map[string]interface{}{"body": "sentinel, sentinel, where have you been?", "msgtype": "m.text"},
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Log("Nigel does an initial sliding sync.")
|
|
|
|
nigelRes := nigel.SlidingSync(t, sync3.Request{
|
|
|
|
Lists: map[string]sync3.RequestList{
|
|
|
|
"a": {
|
|
|
|
RoomSubscription: sync3.RoomSubscription{
|
|
|
|
TimelineLimit: 20,
|
|
|
|
},
|
|
|
|
Ranges: sync3.SliceRanges{{0, 10}},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
t.Log("Nigel sees the sentinel.")
|
|
|
|
m.MatchResponse(t, nigelRes, m.MatchRoomSubscription(dm, MatchRoomTimelineMostRecent(1, []Event{{ID: dmSentinel}})))
|
|
|
|
|
|
|
|
t.Log("Alice pre-emptively bans Nigel from the public room.")
|
|
|
|
alice.MustDo(t, "POST", []string{"_matrix", "client", "v3", "rooms", public, "ban"},
|
|
|
|
client.WithJSONBody(t, map[string]any{"user_id": nigel.UserID}))
|
|
|
|
|
|
|
|
t.Log("Alice sliding syncs until she sees the ban.")
|
|
|
|
alice.SlidingSyncUntilMembership(t, "", public, nigel, "ban")
|
|
|
|
|
|
|
|
t.Log("Alice sends a second sentinel in Nigel's DM.")
|
|
|
|
dmSentinel2 := alice.SendEventSynced(t, dm, b.Event{
|
|
|
|
Type: "m.room.message",
|
|
|
|
Content: map[string]interface{}{"body": "sentinel 2 placeholder boogaloo", "msgtype": "m.text"},
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Log("Nigel syncs until he sees the second sentinel. He should NOT see his ban event.")
|
|
|
|
|
|
|
|
nigelRes = nigel.SlidingSyncUntil(t, nigelRes.Pos, sync3.Request{}, func(response *sync3.Response) error {
|
|
|
|
seenPublicRoom := m.MatchRoomSubscription(public)
|
|
|
|
if seenPublicRoom(response) == nil {
|
|
|
|
t.Errorf("Nigel had a room subscription for the public room, but shouldn't have.")
|
|
|
|
m.LogResponse(t)(response)
|
|
|
|
t.FailNow()
|
|
|
|
}
|
|
|
|
seenSentinel := m.MatchRoomSubscription(dm, MatchRoomTimelineMostRecent(1, []Event{{ID: dmSentinel2}}))
|
|
|
|
return seenSentinel(response)
|
|
|
|
})
|
|
|
|
}
|