sliding-sync/tests-e2e/account_data_test.go

295 lines
8.9 KiB
Go
Raw Permalink Normal View History

2023-03-31 15:28:03 +01:00
package syncv3_test
import (
"encoding/json"
2023-08-02 10:30:29 +01:00
"testing"
"time"
"github.com/matrix-org/complement/b"
2023-03-31 15:28:03 +01:00
"github.com/matrix-org/sliding-sync/sync3"
"github.com/matrix-org/sliding-sync/sync3/extensions"
"github.com/matrix-org/sliding-sync/testutils"
"github.com/matrix-org/sliding-sync/testutils/m"
)
func TestAccountDataRespectsExtensionScope(t *testing.T) {
alice := registerNewUser(t)
var syncResp *sync3.Response
// Want at least one test of the initial sync behaviour (which hits `ProcessInitial`)
// separate to the incremental sync behaviour (hits `AppendLive`)
t.Log("Alice creates rooms 1 and 2.")
2023-10-11 12:23:46 +01:00
room1 := alice.MustCreateRoom(t, map[string]interface{}{"preset": "public_chat", "name": "room 1"})
room2 := alice.MustCreateRoom(t, map[string]interface{}{"preset": "public_chat", "name": "room 2"})
2023-03-31 15:28:03 +01:00
t.Logf("room1=%s room2=%s", room1, room2)
t.Log("Alice uploads account data for both rooms, plus global account data.")
globalAccountDataEvent := putGlobalAccountData(
t,
alice,
"com.example.global",
2023-03-31 17:28:08 +01:00
map[string]interface{}{"global": "GLOBAL!", "version": 1},
2023-03-31 15:28:03 +01:00
)
putRoomAccountData(
t,
alice,
room1,
"com.example.room",
2023-03-31 17:28:08 +01:00
map[string]interface{}{"room": 1, "version": 1},
2023-03-31 15:28:03 +01:00
)
putRoomAccountData(
t,
alice,
room2,
"com.example.room",
2023-03-31 17:28:08 +01:00
map[string]interface{}{"room": 2, "version": 2},
2023-03-31 15:28:03 +01:00
)
t.Log("Alice makes an initial sync request, requesting global account data only.")
syncResp = alice.SlidingSync(t, sync3.Request{
Extensions: extensions.Request{
AccountData: &extensions.AccountDataRequest{
Core: extensions.Core{Enabled: &boolTrue, Lists: []string{}, Rooms: []string{}},
},
},
Lists: map[string]sync3.RequestList{
"window": {
Ranges: sync3.SliceRanges{{0, 20}},
},
},
})
t.Log("Alice should see her global account data only.")
m.MatchResponse(
t,
syncResp,
2023-03-31 17:28:08 +01:00
m.MatchHasGlobalAccountData(globalAccountDataEvent),
2023-03-31 15:28:03 +01:00
m.MatchNoRoomAccountData([]string{room1, room2}),
)
2023-03-31 17:28:08 +01:00
2023-04-03 19:50:17 +01:00
t.Log("Alice updates her global account data.")
2023-03-31 17:28:08 +01:00
globalAccountDataEvent = putGlobalAccountData(
t,
alice,
"com.example.global",
map[string]interface{}{"global": "GLOBAL!", "version": 2},
)
2023-04-03 19:50:17 +01:00
t.Log("Alice syncs until she sees the new data, requesting—but not expecting—account data for room 2.")
syncResp = alice.SlidingSyncUntil(
2023-03-31 17:28:08 +01:00
t,
2023-04-03 19:50:17 +01:00
syncResp.Pos,
sync3.Request{
Extensions: extensions.Request{
AccountData: &extensions.AccountDataRequest{
Core: extensions.Core{Enabled: &boolTrue, Lists: []string{}, Rooms: []string{room2}},
},
},
},
func(response *sync3.Response) error {
err := m.MatchNoRoomAccountData([]string{room1, room2})(response)
if err != nil {
t.Error(err)
}
return m.MatchHasGlobalAccountData(globalAccountDataEvent)(response)
},
2023-03-31 17:28:08 +01:00
)
t.Log("Alice updates account data in both rooms")
putRoomAccountData(
t,
alice,
room1,
"com.example.room",
map[string]interface{}{"room": 1, "version": 11},
2023-03-31 17:28:08 +01:00
)
room2AccountDataEvent := putRoomAccountData(
t,
alice,
room2,
"com.example.room",
map[string]interface{}{"room": 2, "version": 22},
2023-03-31 17:28:08 +01:00
)
2023-04-03 19:50:17 +01:00
t.Log("Alice syncs until she sees the account data for room 2. She shouldn't see account data for room 1")
syncResp = alice.SlidingSyncUntil(
2023-03-31 17:28:08 +01:00
t,
2023-04-03 19:50:17 +01:00
syncResp.Pos,
sync3.Request{}, // as before
func(response *sync3.Response) error {
err := m.MatchNoRoomAccountData([]string{room1})(response)
if err != nil {
t.Error(err)
}
return m.MatchAccountData(nil, map[string][]json.RawMessage{room2: {room2AccountDataEvent}})(response)
},
2023-03-31 17:28:08 +01:00
)
2023-08-02 10:30:29 +01:00
}
// Regression test for https://github.com/matrix-org/sliding-sync/issues/189
func TestAccountDataDoesntDupe(t *testing.T) {
alice := registerNewUser(t)
alice2 := *alice
alice2.Login(t, "password", "device2")
// send some initial account data
putGlobalAccountData(t, alice, "initial", map[string]interface{}{"foo": "bar"})
// no devices are polling.
// syncing with both devices => only shows 1 copy of this event per connection
for _, client := range []*CSAPI{alice, &alice2} {
res := client.SlidingSync(t, sync3.Request{
Extensions: extensions.Request{
AccountData: &extensions.AccountDataRequest{
Core: extensions.Core{
Enabled: &boolTrue,
},
},
},
})
m.MatchResponse(t, res, MatchGlobalAccountData([]Event{
{
Type: "m.push_rules",
},
{
Type: "initial",
Content: map[string]interface{}{"foo": "bar"},
},
}))
}
2023-03-31 17:28:08 +01:00
2023-08-02 10:30:29 +01:00
// now both devices are polling, we're going to do the same thing to make sure we only see only 1 copy still.
putGlobalAccountData(t, alice, "initial2", map[string]interface{}{"foo2": "bar2"})
time.Sleep(time.Second) // TODO: we need to make sure the pollers have seen this and explciitly don't want to use SlidingSyncUntil...
var responses []*sync3.Response
for _, client := range []*CSAPI{alice, &alice2} {
res := client.SlidingSync(t, sync3.Request{
Extensions: extensions.Request{
AccountData: &extensions.AccountDataRequest{
Core: extensions.Core{
Enabled: &boolTrue,
},
},
},
})
m.MatchResponse(t, res, MatchGlobalAccountData([]Event{
{
Type: "m.push_rules",
},
{
Type: "initial",
Content: map[string]interface{}{"foo": "bar"},
},
{
Type: "initial2",
Content: map[string]interface{}{"foo2": "bar2"},
},
}))
responses = append(responses, res) // we need the pos values later
}
// now we're going to do an incremental sync with account data to make sure we don't see dupes either.
putGlobalAccountData(t, alice, "incremental", map[string]interface{}{"foo3": "bar3"})
time.Sleep(time.Second) // TODO: we need to make sure the pollers have seen this and explciitly don't want to use SlidingSyncUntil...
for i, client := range []*CSAPI{alice, &alice2} {
res := client.SlidingSync(t, sync3.Request{
Extensions: extensions.Request{
AccountData: &extensions.AccountDataRequest{
Core: extensions.Core{
Enabled: &boolTrue,
},
},
},
}, WithPos(responses[i].Pos))
m.MatchResponse(t, res, MatchGlobalAccountData([]Event{
{
Type: "incremental",
Content: map[string]interface{}{"foo3": "bar3"},
},
}))
}
2023-03-31 15:28:03 +01:00
}
// Regression test for https://github.com/matrix-org/sliding-sync/issues/417
func TestAccountDataDoesntDupeWithUpdates(t *testing.T) {
alice := registerNewUser(t)
roomID := alice.MustCreateRoom(t, map[string]interface{}{
"preset": "public_chat",
})
roomAccountData1 := putRoomAccountData(t, alice, roomID, "foo", map[string]interface{}{"a": "b"})
roomAccountData2 := putRoomAccountData(t, alice, roomID, "bar", map[string]interface{}{"a2": "b2"})
// all_rooms with timeline limit 1 initially
res := alice.SlidingSync(t, sync3.Request{
Extensions: extensions.Request{
AccountData: &extensions.AccountDataRequest{
Core: extensions.Core{
Enabled: &boolTrue,
},
},
},
Lists: map[string]sync3.RequestList{
"all_rooms": {
Ranges: sync3.SliceRanges{{0, 10}},
RoomSubscription: sync3.RoomSubscription{
TimelineLimit: 1,
},
},
},
})
m.MatchResponse(t, res, m.MatchList("all_rooms", m.MatchV3Count(1)), m.MatchAccountData(nil, map[string][]json.RawMessage{
roomID: {roomAccountData1, roomAccountData2},
}))
// now send some events into the room
alice.SendTyping(t, roomID, true, 5000)
alice.SendEventSynced(t, roomID, b.Event{
Type: "custom",
Content: map[string]interface{}{
"foo": "bar",
},
})
// now sync with a visible_rooms list, we should not see duplicate account data (though do expect 2 as we're changing the subscription)
res = alice.SlidingSync(t, sync3.Request{
Lists: map[string]sync3.RequestList{
"all_rooms": {
Ranges: sync3.SliceRanges{{0, 10}},
RoomSubscription: sync3.RoomSubscription{
TimelineLimit: 1,
},
},
"visible_rooms": {
Ranges: sync3.SliceRanges{{0, 10}},
RoomSubscription: sync3.RoomSubscription{
TimelineLimit: 10,
},
},
},
}, WithPos(res.Pos))
m.MatchResponse(t, res, m.MatchList("all_rooms", m.MatchV3Count(1)), m.MatchAccountData(nil, map[string][]json.RawMessage{
roomID: {roomAccountData1, roomAccountData2},
}))
}
2023-03-31 15:28:03 +01:00
// putAccountData is a wrapper around SetGlobalAccountData. It returns the account data
// event as a json.RawMessage, automatically including top-level `type` and `content`
// fields. This is useful because it can be compared with account data events in a
// sync response with bytes.Equal.
func putGlobalAccountData(t *testing.T, client *CSAPI, eventType string, content map[string]interface{}) json.RawMessage {
t.Helper()
2023-10-11 12:23:46 +01:00
client.MustSetGlobalAccountData(t, eventType, content)
2023-03-31 15:28:03 +01:00
serialised := testutils.NewAccountData(t, eventType, content)
return serialised
}
// putRoomAccountData is like putGlobalAccountData, but for room-specific account data.
func putRoomAccountData(t *testing.T, client *CSAPI, roomID, eventType string, content map[string]interface{}) json.RawMessage {
t.Helper()
2023-10-11 12:23:46 +01:00
client.MustSetRoomAccountData(t, roomID, eventType, content)
2023-03-31 15:28:03 +01:00
serialised := testutils.NewAccountData(t, eventType, content)
return serialised
}