Skip to content

Commit ad263aa

Browse files
kegsayS7evinK
andauthored
Add device list update tests over federation (#695)
* Add failing test * Add connectivity tests; fails on stopped server subtest * Remove grace period and increase timeout to let synapse pass * Right device keys * Update tests/federation_device_list_update_test.go Co-authored-by: Till <[email protected]> --------- Co-authored-by: Kegan Dougal <=> Co-authored-by: Till <[email protected]>
1 parent 8a0df24 commit ad263aa

File tree

1 file changed

+160
-0
lines changed

1 file changed

+160
-0
lines changed
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
package tests
2+
3+
import (
4+
"fmt"
5+
"reflect"
6+
"sort"
7+
"testing"
8+
"time"
9+
10+
"github.com/matrix-org/complement"
11+
"github.com/matrix-org/complement/client"
12+
"github.com/matrix-org/complement/helpers"
13+
"github.com/tidwall/gjson"
14+
)
15+
16+
// Test that device list updates can go from one homeserver to another.
17+
func TestDeviceListsUpdateOverFederation(t *testing.T) {
18+
deployment := complement.Deploy(t, 2)
19+
defer deployment.Destroy(t)
20+
21+
syncHasDeviceListChange := func(changed []string, left []string) client.SyncCheckOpt {
22+
sort.Strings(changed)
23+
sort.Strings(left)
24+
return func(clientUserID string, topLevelSyncJSON gjson.Result) error {
25+
dl := topLevelSyncJSON.Get("device_lists")
26+
changedJSON := dl.Get("changed").Array()
27+
leftJSON := dl.Get("left").Array()
28+
gotChanged := make([]string, len(changedJSON))
29+
gotLeft := make([]string, len(leftJSON))
30+
for i := range gotChanged {
31+
gotChanged[i] = changedJSON[i].Str
32+
}
33+
for i := range gotLeft {
34+
gotLeft[i] = leftJSON[i].Str
35+
}
36+
sort.Strings(gotChanged)
37+
sort.Strings(gotLeft)
38+
changedMatch := reflect.DeepEqual(changed, gotChanged)
39+
leftMatch := reflect.DeepEqual(left, gotLeft)
40+
if changedMatch && leftMatch {
41+
return nil
42+
}
43+
return fmt.Errorf("syncHasDeviceListChange: got changed %v want %v, got left %v want %v", gotChanged, changed, gotLeft, left)
44+
}
45+
}
46+
47+
testCases := []struct {
48+
name string
49+
makeUnreachable func(t *testing.T)
50+
makeReachable func(t *testing.T)
51+
}{
52+
{
53+
name: "good connectivity",
54+
makeUnreachable: func(t *testing.T) {},
55+
makeReachable: func(t *testing.T) {},
56+
},
57+
{
58+
// cut networking but keep in-memory state
59+
name: "interrupted connectivity",
60+
makeUnreachable: func(t *testing.T) {
61+
deployment.StopServer(t, "hs2")
62+
},
63+
makeReachable: func(t *testing.T) {
64+
deployment.StartServer(t, "hs2")
65+
},
66+
},
67+
{
68+
// interesting because this nukes memory
69+
name: "stopped server",
70+
makeUnreachable: func(t *testing.T) {
71+
deployment.StopServer(t, "hs2")
72+
},
73+
makeReachable: func(t *testing.T) {
74+
// kick over the sending server first to see if the server
75+
// remembers to resend on startup
76+
deployment.StopServer(t, "hs1")
77+
deployment.StartServer(t, "hs1")
78+
// now make the receiving server reachable.
79+
deployment.StartServer(t, "hs2")
80+
},
81+
},
82+
}
83+
84+
for _, tc := range testCases {
85+
tc := tc
86+
t.Run(tc.name, func(t *testing.T) {
87+
alice := deployment.Register(t, "hs1", helpers.RegistrationOpts{
88+
LocalpartSuffix: "alice",
89+
Password: "this is alices password",
90+
})
91+
bob := deployment.Register(t, "hs2", helpers.RegistrationOpts{
92+
LocalpartSuffix: "bob",
93+
})
94+
// they must share a room to get device list updates
95+
roomID := alice.MustCreateRoom(t, map[string]interface{}{
96+
"preset": "private_chat",
97+
"invite": []string{bob.UserID},
98+
// we strictly don't need to make this an encrypted room, but there are some
99+
// issues which state that we should only share device list updates for
100+
// users in shared encrypted rooms, so let's ensure we do that.
101+
// See https://github.com/matrix-org/synapse/issues/7524
102+
"initial_state": []map[string]interface{}{
103+
{
104+
"type": "m.room.encryption",
105+
"state_key": "",
106+
"content": map[string]interface{}{
107+
"algorithm": "m.megolm.v1.aes-sha2",
108+
},
109+
},
110+
},
111+
})
112+
// it might take a while for retries, so keep on syncing!
113+
bob.SyncUntilTimeout = 50 * time.Second
114+
_, aliceSince := alice.MustSync(t, client.SyncReq{TimeoutMillis: "0"})
115+
bobSince := bob.MustSyncUntil(t, client.SyncReq{TimeoutMillis: "0"}, client.SyncInvitedTo(bob.UserID, roomID))
116+
117+
bob.MustJoinRoom(t, roomID, []string{"hs1"})
118+
119+
// both alice and bob should see device list updates for each other
120+
aliceSince = alice.MustSyncUntil(
121+
t, client.SyncReq{TimeoutMillis: "1000", Since: aliceSince},
122+
syncHasDeviceListChange([]string{bob.UserID}, []string{}),
123+
)
124+
bobSince = bob.MustSyncUntil(
125+
t, client.SyncReq{TimeoutMillis: "1000", Since: bobSince},
126+
// bob is in this list because... his other devices may need to know.
127+
syncHasDeviceListChange([]string{alice.UserID, bob.UserID}, []string{}),
128+
)
129+
130+
// now federation is going to be interrupted...
131+
tc.makeUnreachable(t)
132+
133+
// ..and alice logs in on a new device!
134+
alice2 := deployment.Login(t, "hs1", alice, helpers.LoginOpts{
135+
DeviceID: "NEW_DEVICE",
136+
Password: "this is alices password",
137+
})
138+
deviceKeys, oneTimeKeys := alice2.MustGenerateOneTimeKeys(t, 1)
139+
alice2.MustDo(t, "POST", []string{"_matrix", "client", "v3", "keys", "upload"}, client.WithJSONBody(t, map[string]interface{}{
140+
"device_keys": deviceKeys,
141+
"one_time_keys": oneTimeKeys,
142+
}))
143+
144+
// now federation comes back online
145+
tc.makeReachable(t)
146+
147+
// ensure alice sees her new device login
148+
aliceSince = alice.MustSyncUntil(
149+
t, client.SyncReq{TimeoutMillis: "1000", Since: aliceSince},
150+
syncHasDeviceListChange([]string{alice.UserID}, []string{}),
151+
)
152+
153+
// ensure bob sees the device list change
154+
bobSince = bob.MustSyncUntil(
155+
t, client.SyncReq{TimeoutMillis: "1000", Since: bobSince},
156+
syncHasDeviceListChange([]string{alice.UserID}, []string{}),
157+
)
158+
})
159+
}
160+
}

0 commit comments

Comments
 (0)