Skip to content

Commit 8a0df24

Browse files
kegsayS7evinK
andauthored
Add to-device event tests over federation; incl. connectivity tests (#694)
* Add to-device event tests over federation; incl. connectivity tests The connectivity tests form part of complement-crypto's test suite, specifically: > If a server cannot send device list updates over federation, it retries. * Test we retry on startup * Comment why we need to poke * Update tests/federation_to_device_test.go Co-authored-by: Till <[email protected]> --------- Co-authored-by: Kegan Dougal <=> Co-authored-by: Till <[email protected]>
1 parent 25a2d5c commit 8a0df24

File tree

2 files changed

+133
-0
lines changed

2 files changed

+133
-0
lines changed

internal/docker/deployment.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@ func (d *Deployment) Restart(t *testing.T) error {
246246

247247
func (d *Deployment) StartServer(t *testing.T, hsName string) {
248248
t.Helper()
249+
t.Logf("StartServer %s", hsName)
249250
hsDep := d.HS[hsName]
250251
if hsDep == nil {
251252
t.Fatalf("StartServer: %s does not exist in this deployment", hsName)
@@ -257,6 +258,7 @@ func (d *Deployment) StartServer(t *testing.T, hsName string) {
257258

258259
func (d *Deployment) StopServer(t *testing.T, hsName string) {
259260
t.Helper()
261+
t.Logf("StopServer %s", hsName)
260262
hsDep := d.HS[hsName]
261263
if hsDep == nil {
262264
t.Fatalf("StopServer: %s does not exist in this deployment", hsName)
@@ -268,6 +270,7 @@ func (d *Deployment) StopServer(t *testing.T, hsName string) {
268270

269271
func (d *Deployment) PauseServer(t *testing.T, hsName string) {
270272
t.Helper()
273+
t.Logf("PauseServer %s", hsName)
271274
hsDep := d.HS[hsName]
272275
if hsDep == nil {
273276
t.Fatalf("PauseServer: %s does not exist in this deployment", hsName)
@@ -279,6 +282,7 @@ func (d *Deployment) PauseServer(t *testing.T, hsName string) {
279282

280283
func (d *Deployment) UnpauseServer(t *testing.T, hsName string) {
281284
t.Helper()
285+
t.Logf("UnpauseServer %s", hsName)
282286
hsDep := d.HS[hsName]
283287
if hsDep == nil {
284288
t.Fatalf("UnpauseServer: %s does not exist in this deployment", hsName)

tests/federation_to_device_test.go

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package tests
2+
3+
import (
4+
"reflect"
5+
"sync/atomic"
6+
"testing"
7+
"time"
8+
9+
"github.com/matrix-org/complement"
10+
"github.com/matrix-org/complement/client"
11+
"github.com/matrix-org/complement/helpers"
12+
"github.com/tidwall/gjson"
13+
)
14+
15+
// Test that to-device messages can go from one homeserver to another.
16+
func TestToDeviceMessagesOverFederation(t *testing.T) {
17+
deployment := complement.Deploy(t, 2)
18+
defer deployment.Destroy(t)
19+
20+
testCases := []struct {
21+
name string
22+
makeUnreachable func(t *testing.T)
23+
makeReachable func(t *testing.T)
24+
}{
25+
{
26+
name: "good connectivity",
27+
makeUnreachable: func(t *testing.T) {},
28+
makeReachable: func(t *testing.T) {},
29+
},
30+
{
31+
// cut networking but keep in-memory state
32+
name: "interrupted connectivity",
33+
makeUnreachable: func(t *testing.T) {
34+
deployment.StopServer(t, "hs2")
35+
},
36+
makeReachable: func(t *testing.T) {
37+
deployment.StartServer(t, "hs2")
38+
},
39+
},
40+
{
41+
// interesting because this nukes memory
42+
name: "stopped server",
43+
makeUnreachable: func(t *testing.T) {
44+
deployment.StopServer(t, "hs2")
45+
},
46+
makeReachable: func(t *testing.T) {
47+
// kick over the sending server first to see if the server
48+
// remembers to resend on startup
49+
deployment.StopServer(t, "hs1")
50+
deployment.StartServer(t, "hs1")
51+
// now make the receiving server reachable.
52+
deployment.StartServer(t, "hs2")
53+
},
54+
},
55+
}
56+
57+
for _, tc := range testCases {
58+
tc := tc
59+
t.Run(tc.name, func(t *testing.T) {
60+
alice := deployment.Register(t, "hs1", helpers.RegistrationOpts{
61+
LocalpartSuffix: "alice",
62+
})
63+
bob := deployment.Register(t, "hs2", helpers.RegistrationOpts{
64+
LocalpartSuffix: "bob",
65+
})
66+
// it might take a while for retries, so keep on syncing!
67+
bob.SyncUntilTimeout = 30 * time.Second
68+
69+
_, bobSince := bob.MustSync(t, client.SyncReq{TimeoutMillis: "0"})
70+
71+
content := map[string]interface{}{
72+
"my_key": "my_value",
73+
}
74+
75+
tc.makeUnreachable(t)
76+
77+
alice.MustSendToDeviceMessages(t, "my.test.type", map[string]map[string]map[string]interface{}{
78+
bob.UserID: {
79+
bob.DeviceID: content,
80+
},
81+
})
82+
83+
checkEvent := func(result gjson.Result) bool {
84+
if result.Get("type").Str != "my.test.type" {
85+
return false
86+
}
87+
88+
evContentRes := result.Get("content")
89+
90+
if !evContentRes.Exists() || !evContentRes.IsObject() {
91+
return false
92+
}
93+
94+
evContent := evContentRes.Value()
95+
96+
return reflect.DeepEqual(evContent, content)
97+
}
98+
// just in case the server returns 200 OK before flushing to disk, give it a grace period.
99+
// This is too nice of us given in the real world no grace is provided..
100+
time.Sleep(time.Second)
101+
102+
tc.makeReachable(t)
103+
104+
// servers may need to be poked with another to-device msg. This isn't great.
105+
// See https://github.com/matrix-org/synapse/issues/16680
106+
// bob has a sync timeout of 30s set, so if the test has not yet passed, we are kicking the server
107+
// after 10s to ensure the server processes the previous sent to-device message.
108+
var completed atomic.Bool
109+
go func() {
110+
time.Sleep(10 * time.Second)
111+
if completed.Load() {
112+
return
113+
}
114+
// maybe kicking the server will make things work if we're still waiting after 10s
115+
alice.MustSendToDeviceMessages(t, "kick.type", map[string]map[string]map[string]interface{}{
116+
bob.UserID: {
117+
bob.DeviceID: content,
118+
},
119+
})
120+
}()
121+
122+
bob.MustSyncUntil(t, client.SyncReq{Since: bobSince}, func(clientUserID string, topLevelSyncJSON gjson.Result) error {
123+
t.Logf("%s", topLevelSyncJSON.Raw)
124+
return client.SyncToDeviceHas(alice.UserID, checkEvent)(clientUserID, topLevelSyncJSON)
125+
})
126+
completed.Store(true)
127+
})
128+
}
129+
}

0 commit comments

Comments
 (0)