22
22
23
23
import synapse .rest .admin
24
24
import synapse .storage
25
- from synapse .api .constants import EduTypes
25
+ from synapse .api .constants import EduTypes , EventTypes
26
26
from synapse .appservice import (
27
27
ApplicationService ,
28
28
TransactionOneTimeKeyCounts ,
36
36
from synapse .util .stringutils import random_string
37
37
38
38
from tests import unittest
39
- from tests .test_utils import make_awaitable , simple_async_mock
39
+ from tests .test_utils import event_injection , make_awaitable , simple_async_mock
40
40
from tests .unittest import override_config
41
41
from tests .utils import MockClock
42
42
@@ -390,15 +390,16 @@ class ApplicationServicesHandlerSendEventsTestCase(unittest.HomeserverTestCase):
390
390
receipts .register_servlets ,
391
391
]
392
392
393
- def prepare (self , reactor , clock , hs ):
393
+ def prepare (self , reactor : MemoryReactor , clock : Clock , hs : HomeServer ):
394
+ self .hs = hs
394
395
# Mock the ApplicationServiceScheduler's _TransactionController's send method so that
395
396
# we can track any outgoing ephemeral events
396
397
self .send_mock = simple_async_mock ()
397
- hs .get_application_service_handler ().scheduler .txn_ctrl .send = self .send_mock
398
+ hs .get_application_service_handler ().scheduler .txn_ctrl .send = self .send_mock # type: ignore[assignment]
398
399
399
400
# Mock out application services, and allow defining our own in tests
400
401
self ._services : List [ApplicationService ] = []
401
- self .hs .get_datastores ().main .get_app_services = Mock (
402
+ self .hs .get_datastores ().main .get_app_services = Mock ( # type: ignore[assignment]
402
403
return_value = self ._services
403
404
)
404
405
@@ -416,6 +417,157 @@ def prepare(self, reactor, clock, hs):
416
417
"exclusive_as_user" , "password" , self .exclusive_as_user_device_id
417
418
)
418
419
420
+ def _notify_interested_services (self ):
421
+ # This is normally set in `notify_interested_services` but we need to call the
422
+ # internal async version so the reactor gets pushed to completion.
423
+ self .hs .get_application_service_handler ().current_max += 1
424
+ self .get_success (
425
+ self .hs .get_application_service_handler ()._notify_interested_services (
426
+ RoomStreamToken (
427
+ None , self .hs .get_application_service_handler ().current_max
428
+ )
429
+ )
430
+ )
431
+
432
+ @parameterized .expand (
433
+ [
434
+ ("@local_as_user:test" , True ),
435
+ # Defining remote users in an application service user namespace regex is a
436
+ # footgun since the appservice might assume that it'll receive all events
437
+ # sent by that remote user, but it will only receive events in rooms that
438
+ # are shared with a local user. So we just remove this footgun possibility
439
+ # entirely and we won't notify the application service based on remote
440
+ # users.
441
+ ("@remote_as_user:remote" , False ),
442
+ ]
443
+ )
444
+ def test_match_interesting_room_members (
445
+ self , interesting_user : str , should_notify : bool
446
+ ):
447
+ """
448
+ Test to make sure that a interesting user (local or remote) in the room is
449
+ notified as expected when someone else in the room sends a message.
450
+ """
451
+ # Register an application service that's interested in the `interesting_user`
452
+ interested_appservice = self ._register_application_service (
453
+ namespaces = {
454
+ ApplicationService .NS_USERS : [
455
+ {
456
+ "regex" : interesting_user ,
457
+ "exclusive" : False ,
458
+ },
459
+ ],
460
+ },
461
+ )
462
+
463
+ # Create a room
464
+ alice = self .register_user ("alice" , "pass" )
465
+ alice_access_token = self .login ("alice" , "pass" )
466
+ room_id = self .helper .create_room_as (room_creator = alice , tok = alice_access_token )
467
+
468
+ # Join the interesting user to the room
469
+ self .get_success (
470
+ event_injection .inject_member_event (
471
+ self .hs , room_id , interesting_user , "join"
472
+ )
473
+ )
474
+ # Kick the appservice into checking this membership event to get the event out
475
+ # of the way
476
+ self ._notify_interested_services ()
477
+ # We don't care about the interesting user join event (this test is making sure
478
+ # the next thing works)
479
+ self .send_mock .reset_mock ()
480
+
481
+ # Send a message from an uninteresting user
482
+ self .helper .send_event (
483
+ room_id ,
484
+ type = EventTypes .Message ,
485
+ content = {
486
+ "msgtype" : "m.text" ,
487
+ "body" : "message from uninteresting user" ,
488
+ },
489
+ tok = alice_access_token ,
490
+ )
491
+ # Kick the appservice into checking this new event
492
+ self ._notify_interested_services ()
493
+
494
+ if should_notify :
495
+ self .send_mock .assert_called_once ()
496
+ (
497
+ service ,
498
+ events ,
499
+ _ephemeral ,
500
+ _to_device_messages ,
501
+ _otks ,
502
+ _fbks ,
503
+ _device_list_summary ,
504
+ ) = self .send_mock .call_args [0 ]
505
+
506
+ # Even though the message came from an uninteresting user, it should still
507
+ # notify us because the interesting user is joined to the room where the
508
+ # message was sent.
509
+ self .assertEqual (service , interested_appservice )
510
+ self .assertEqual (events [0 ]["type" ], "m.room.message" )
511
+ self .assertEqual (events [0 ]["sender" ], alice )
512
+ else :
513
+ self .send_mock .assert_not_called ()
514
+
515
+ def test_application_services_receive_events_sent_by_interesting_local_user (self ):
516
+ """
517
+ Test to make sure that a messages sent from a local user can be interesting and
518
+ picked up by the appservice.
519
+ """
520
+ # Register an application service that's interested in all local users
521
+ interested_appservice = self ._register_application_service (
522
+ namespaces = {
523
+ ApplicationService .NS_USERS : [
524
+ {
525
+ "regex" : ".*" ,
526
+ "exclusive" : False ,
527
+ },
528
+ ],
529
+ },
530
+ )
531
+
532
+ # Create a room
533
+ alice = self .register_user ("alice" , "pass" )
534
+ alice_access_token = self .login ("alice" , "pass" )
535
+ room_id = self .helper .create_room_as (room_creator = alice , tok = alice_access_token )
536
+
537
+ # We don't care about interesting events before this (this test is making sure
538
+ # the next thing works)
539
+ self .send_mock .reset_mock ()
540
+
541
+ # Send a message from the interesting local user
542
+ self .helper .send_event (
543
+ room_id ,
544
+ type = EventTypes .Message ,
545
+ content = {
546
+ "msgtype" : "m.text" ,
547
+ "body" : "message from interesting local user" ,
548
+ },
549
+ tok = alice_access_token ,
550
+ )
551
+ # Kick the appservice into checking this new event
552
+ self ._notify_interested_services ()
553
+
554
+ self .send_mock .assert_called_once ()
555
+ (
556
+ service ,
557
+ events ,
558
+ _ephemeral ,
559
+ _to_device_messages ,
560
+ _otks ,
561
+ _fbks ,
562
+ _device_list_summary ,
563
+ ) = self .send_mock .call_args [0 ]
564
+
565
+ # Events sent from an interesting local user should also be picked up as
566
+ # interesting to the appservice.
567
+ self .assertEqual (service , interested_appservice )
568
+ self .assertEqual (events [0 ]["type" ], "m.room.message" )
569
+ self .assertEqual (events [0 ]["sender" ], alice )
570
+
419
571
def test_sending_read_receipt_batches_to_application_services (self ):
420
572
"""Tests that a large batch of read receipts are sent correctly to
421
573
interested application services.
0 commit comments