Skip to content

Commit a9f76c2

Browse files
committed
Add regression test to ensure we batch /sendToDevice
Fixes #34 Regression test for element-hq/element-web#24680
1 parent 17c6375 commit a9f76c2

File tree

2 files changed

+69
-7
lines changed

2 files changed

+69
-7
lines changed

tests/main_test.go

-7
Original file line numberDiff line numberDiff line change
@@ -109,13 +109,6 @@ func MustCreateClient(t *testing.T, clientType api.ClientType, cfg api.ClientCre
109109
return c
110110
}
111111

112-
// WithDoLogin is an option which can be provided to MustCreateClient which will automatically login, else fail the test.
113-
func WithDoLogin(t *testing.T) func(api.Client, *api.ClientCreationOpts) {
114-
return func(c api.Client, opts *api.ClientCreationOpts) {
115-
must.NotError(t, "failed to login", c.Login(t, *opts))
116-
}
117-
}
118-
119112
// WithPersistentStorage is an option which can be provided to MustCreateClient which will configure clients to use persistent storage,
120113
// e.g IndexedDB or sqlite3 files.
121114
func WithPersistentStorage() func(*api.ClientCreationOpts) {

tests/to_device_test.go

+69
Original file line numberDiff line numberDiff line change
@@ -241,3 +241,72 @@ func testUnprocessedToDeviceMessagesArentLostOnRestartJS(t *testing.T, tc *TestC
241241
must.Equal(t, ev.Text, "Kick to make a new room key!", "event text mismatch")
242242
})
243243
}
244+
245+
// Regression test for https://github.com/element-hq/element-web/issues/24680
246+
//
247+
// It's important that room keys are sent out ASAP, else the encrypted event may arrive
248+
// before the keys, causing a temporary unable-to-decrypt error. Clients SHOULD be batching
249+
// to-device messages, but old implementations batched too low (20 messages per request).
250+
// This test asserts we batch at least 100 per request.
251+
//
252+
// It does this by creating an E2EE room with 100 E2EE users, and forces a key rotation
253+
// by sending a message with rotation_period_msgs=1. It does not ensure that the room key
254+
// is correctly sent to all 100 users as that would entail having 100 users running at
255+
// the same time (think 100 browsers = expensive). Instead, we sequentially spin up 100
256+
// clients and then close them before doing the test, and assert we send 100 events.
257+
//
258+
// In the future, it may be difficult to run this test for 1 user with 100 devices due to
259+
// HS limits on the number of devices and forced cross-signing.
260+
func TestToDeviceMessagesAreBatched(t *testing.T) {
261+
ForEachClientType(t, func(t *testing.T, clientType api.ClientType) {
262+
tc := CreateTestContext(t, clientType)
263+
roomID := tc.CreateNewEncryptedRoom(t, tc.Alice, EncRoomOptions.RotationPeriodMsgs(1), EncRoomOptions.PresetPublicChat())
264+
// create 100 users
265+
for i := 0; i < 100; i++ {
266+
cli := tc.Deployment.Register(t, clientType.HS, helpers.RegistrationOpts{
267+
LocalpartSuffix: fmt.Sprintf("bob-%d", i),
268+
Password: "complement-crypto-password",
269+
})
270+
cli.MustJoinRoom(t, roomID, []string{clientType.HS})
271+
// this blocks until it has uploaded OTKs/device keys
272+
clientUnderTest := tc.MustLoginClient(t, cli, tc.AliceClientType)
273+
clientUnderTest.Close(t)
274+
}
275+
waiter := helpers.NewWaiter()
276+
tc.WithAliceSyncing(t, func(alice api.Client) {
277+
// intercept /sendToDevice and check we are sending 100 messages per request
278+
tc.Deployment.WithSniffedEndpoint(t, "/sendToDevice", func(cd deploy.CallbackData) {
279+
if cd.Method != "PUT" {
280+
return
281+
}
282+
// format is:
283+
/*
284+
{
285+
"messages": {
286+
"@alice:example.com": {
287+
"TLLBEANAAG": {
288+
"example_content_key": "value"
289+
}
290+
}
291+
}
292+
}
293+
*/
294+
usersMap := gjson.GetBytes(cd.RequestBody, "messages")
295+
if !usersMap.Exists() {
296+
t.Logf("intercepted PUT /sendToDevice but no messages existed")
297+
return
298+
}
299+
if len(usersMap.Map()) != 100 {
300+
t.Errorf("PUT /sendToDevice did not batch messages, got %d want 100", len(usersMap.Map()))
301+
t.Logf(usersMap.Raw)
302+
}
303+
waiter.Finish()
304+
}, func() {
305+
alice.SendMessage(t, roomID, "this should cause to-device msgs to be sent")
306+
time.Sleep(time.Second)
307+
waiter.Waitf(t, 5*time.Second, "did not see /sendToDevice")
308+
})
309+
})
310+
311+
})
312+
}

0 commit comments

Comments
 (0)