@@ -411,6 +411,88 @@ def prepare(self, reactor, clock, hs):
411
411
"exclusive_as_user" , "password" , self .exclusive_as_user_device_id
412
412
)
413
413
414
+ def test_sending_read_receipt_batches_to_application_services (self ):
415
+ """Tests that a large batch of read receipts are sent correctly to
416
+ interested application services.
417
+ """
418
+ # Register an application service that's interested in a certain user
419
+ # and room prefix
420
+ interested_appservice = self ._register_application_service (
421
+ namespaces = {
422
+ ApplicationService .NS_USERS : [
423
+ {
424
+ "regex" : "@exclusive_as_user:.+" ,
425
+ "exclusive" : True ,
426
+ }
427
+ ],
428
+ ApplicationService .NS_ROOMS : [
429
+ {
430
+ "regex" : "!fakeroom_.*" ,
431
+ "exclusive" : True ,
432
+ }
433
+ ],
434
+ },
435
+ )
436
+
437
+ # "Complete" a transaction.
438
+ # All this really does for us is make an entry in the application_services_state
439
+ # database table, which tracks the current stream_token per stream ID per AS.
440
+ self .get_success (
441
+ self .hs .get_datastores ().main .complete_appservice_txn (
442
+ 0 ,
443
+ interested_appservice ,
444
+ )
445
+ )
446
+
447
+ # Now, pretend that we receive a large burst of read receipts (300 total) that
448
+ # all come in at once.
449
+ for i in range (300 ):
450
+ self .get_success (
451
+ # Insert a fake read receipt into the database
452
+ self .hs .get_datastores ().main .insert_receipt (
453
+ # We have to use unique room ID + user ID combinations here, as the db query
454
+ # is an upsert.
455
+ room_id = f"!fakeroom_{ i } :test" ,
456
+ receipt_type = "m.read" ,
457
+ user_id = self .local_user ,
458
+ event_ids = [f"$eventid_{ i } " ],
459
+ data = {},
460
+ )
461
+ )
462
+
463
+ # Now notify the appservice handler that 300 read receipts have all arrived
464
+ # at once. What will it do!
465
+ # note: stream tokens start at 2
466
+ for stream_token in range (2 , 303 ):
467
+ self .get_success (
468
+ self .hs .get_application_service_handler ()._notify_interested_services_ephemeral (
469
+ services = [interested_appservice ],
470
+ stream_key = "receipt_key" ,
471
+ new_token = stream_token ,
472
+ users = [self .exclusive_as_user ],
473
+ )
474
+ )
475
+
476
+ # Using our txn send mock, we can see what the AS received. After iterating over every
477
+ # transaction, we'd like to see all 300 read receipts accounted for.
478
+ # No more, no less.
479
+ all_ephemeral_events = []
480
+ for call in self .send_mock .call_args_list :
481
+ ephemeral_events = call [0 ][2 ]
482
+ all_ephemeral_events += ephemeral_events
483
+
484
+ # Ensure that no duplicate events were sent
485
+ self .assertEqual (len (all_ephemeral_events ), 300 )
486
+
487
+ # Check that the ephemeral event is a read receipt with the expected structure
488
+ latest_read_receipt = all_ephemeral_events [- 1 ]
489
+ self .assertEqual (latest_read_receipt ["type" ], "m.receipt" )
490
+
491
+ event_id = list (latest_read_receipt ["content" ].keys ())[0 ]
492
+ self .assertEqual (
493
+ latest_read_receipt ["content" ][event_id ]["m.read" ], {self .local_user : {}}
494
+ )
495
+
414
496
@unittest .override_config (
415
497
{"experimental_features" : {"msc2409_to_device_messages_enabled" : True }}
416
498
)
0 commit comments