mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-03 21:43:21 +00:00 
			
		
		
		
	maybe_enqueue_notifications: Take in notification_data dataclass.
* Modify `maybe_enqueue_notifications` to take in an instance of the dataclass introduced in951b49c048. * The `check_notify` tests tested the "when to notify" logic in a way which involved `maybe_enqueue_notifications`. To simplify things, we've earlier extracted this logic in8182632d7e. So, we just kill off the `check_notify` test, and keep only those parts which verify the queueing and return value behavior of that funtion. * We retain the the missedmessage_hook and message message_edit_notifications since they are more integration-style. * There's a slightly subtle change with the missedmessage_hook tests. Before this commit, we short-circuited the hook if the sender was muted (5a642cea11). With this commit, we delegate the check to our dataclass methods. So, `maybe_enqueue_notifications` will be called even if the sender was muted, and the test needs to be updated. * In our test helper `get_maybe_enqueue_notifications_parameters` which generates default values for testing `maybe_enqueue_notifications` calls, we keep `message_id`, `sender_id`, and `user_id` as required arguments, so that the tests are super-clear and avoid accidental false positives. * Because `do_update_embedded_data` also sends `update_message` events, we deal with that case with some hacky code for now. See the comment there. This mostly completes the extraction of the "when to notify" logic into our new `notification_data` module.
This commit is contained in:
		
				
					committed by
					
						
						Tim Abbott
					
				
			
			
				
	
			
			
			
						parent
						
							dedc39f139
						
					
				
				
					commit
					66192825c0
				
			@@ -1337,26 +1337,28 @@ Output:
 | 
			
		||||
            sender_is_muted=kwargs.get("sender_is_muted", False),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def get_maybe_enqueue_notifications_parameters(self, **kwargs: Any) -> Dict[str, Any]:
 | 
			
		||||
    def get_maybe_enqueue_notifications_parameters(
 | 
			
		||||
        self, *, message_id: int, user_id: int, sender_id: int, **kwargs: Any
 | 
			
		||||
    ) -> Dict[str, Any]:
 | 
			
		||||
        """
 | 
			
		||||
        Returns a dictionary with the passed parameters, after filling up the
 | 
			
		||||
        missing data with default values, for testing what was passed to the
 | 
			
		||||
        `maybe_enqueue_notifications` method.
 | 
			
		||||
        """
 | 
			
		||||
        parameters: Dict[str, Any] = dict(
 | 
			
		||||
            private_message=False,
 | 
			
		||||
            mentioned=False,
 | 
			
		||||
            wildcard_mention_notify=False,
 | 
			
		||||
            stream_push_notify=False,
 | 
			
		||||
            stream_email_notify=False,
 | 
			
		||||
            stream_name=None,
 | 
			
		||||
            online_push_enabled=False,
 | 
			
		||||
            idle=True,
 | 
			
		||||
            already_notified={"email_notified": False, "push_notified": False},
 | 
			
		||||
        user_notifications_data = self.create_user_notifications_data_object(
 | 
			
		||||
            user_id=user_id, **kwargs
 | 
			
		||||
        )
 | 
			
		||||
        return dict(
 | 
			
		||||
            user_data=user_notifications_data,
 | 
			
		||||
            message_id=message_id,
 | 
			
		||||
            sender_id=sender_id,
 | 
			
		||||
            private_message=kwargs.get("private_message", False),
 | 
			
		||||
            stream_name=kwargs.get("stream_name", None),
 | 
			
		||||
            idle=kwargs.get("idle", True),
 | 
			
		||||
            already_notified=kwargs.get(
 | 
			
		||||
                "already_notified", {"email_notified": False, "push_notified": False}
 | 
			
		||||
            ),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        # Values from `kwargs` will replace those from `parameters`
 | 
			
		||||
        return {**parameters, **kwargs}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class WebhookTestCase(ZulipTestCase):
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
import time
 | 
			
		||||
from typing import Any, Callable, Collection, Dict, List, Optional, Tuple, Union
 | 
			
		||||
from typing import Any, Callable, Collection, Dict, List
 | 
			
		||||
from unittest import mock
 | 
			
		||||
 | 
			
		||||
import orjson
 | 
			
		||||
@@ -25,125 +25,34 @@ class MissedMessageNotificationsTest(ZulipTestCase):
 | 
			
		||||
    """Tests the logic for when missed-message notifications
 | 
			
		||||
    should be triggered, based on user settings"""
 | 
			
		||||
 | 
			
		||||
    def check_will_notify(self, **kwargs: Any) -> Tuple[str, str]:
 | 
			
		||||
        hamlet = self.example_user("hamlet")
 | 
			
		||||
        kwargs["user_profile_id"] = hamlet.id
 | 
			
		||||
        kwargs["message_id"] = 32
 | 
			
		||||
    def test_maybe_enqueue_notifications(self) -> None:
 | 
			
		||||
        # We've already tested the "when to send notifications" logic as part of the
 | 
			
		||||
        # notification_data module.
 | 
			
		||||
        # This test is for verifying whether `maybe_enqueue_notifications` returns the
 | 
			
		||||
        # `already_notified` data correctly.
 | 
			
		||||
        params = self.get_maybe_enqueue_notifications_parameters(
 | 
			
		||||
            message_id=1, user_id=1, sender_id=2
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        # Fill up the parameters which weren't sent with defaults.
 | 
			
		||||
        kwargs = self.get_maybe_enqueue_notifications_parameters(**kwargs)
 | 
			
		||||
 | 
			
		||||
        email_notice = None
 | 
			
		||||
        mobile_notice = None
 | 
			
		||||
        with mock_queue_publish(
 | 
			
		||||
            "zerver.tornado.event_queue.queue_json_publish"
 | 
			
		||||
        ) as mock_queue_json_publish:
 | 
			
		||||
            notified = maybe_enqueue_notifications(**kwargs)
 | 
			
		||||
            for entry in mock_queue_json_publish.call_args_list:
 | 
			
		||||
                args = entry[0]
 | 
			
		||||
                if args[0] == "missedmessage_mobile_notifications":
 | 
			
		||||
                    mobile_notice = args[1]
 | 
			
		||||
                if args[0] == "missedmessage_emails":
 | 
			
		||||
                    email_notice = args[1]
 | 
			
		||||
            notified = maybe_enqueue_notifications(**params)
 | 
			
		||||
            mock_queue_json_publish.assert_not_called()
 | 
			
		||||
 | 
			
		||||
        with mock_queue_publish(
 | 
			
		||||
            "zerver.tornado.event_queue.queue_json_publish"
 | 
			
		||||
        ) as mock_queue_json_publish:
 | 
			
		||||
            params["private_message"] = True
 | 
			
		||||
            notified = maybe_enqueue_notifications(**params)
 | 
			
		||||
            self.assertTrue(mock_queue_json_publish.call_count, 2)
 | 
			
		||||
 | 
			
		||||
            queues_pushed = [entry[0][0] for entry in mock_queue_json_publish.call_args_list]
 | 
			
		||||
            self.assertIn("missedmessage_mobile_notifications", queues_pushed)
 | 
			
		||||
            self.assertIn("missedmessage_emails", queues_pushed)
 | 
			
		||||
 | 
			
		||||
            # Now verify the return value matches the queue actions
 | 
			
		||||
            if email_notice:
 | 
			
		||||
            self.assertTrue(notified["email_notified"])
 | 
			
		||||
            else:
 | 
			
		||||
                self.assertFalse(notified.get("email_notified", False))
 | 
			
		||||
            if mobile_notice:
 | 
			
		||||
            self.assertTrue(notified["push_notified"])
 | 
			
		||||
            else:
 | 
			
		||||
                self.assertFalse(notified.get("push_notified", False))
 | 
			
		||||
        return email_notice, mobile_notice
 | 
			
		||||
 | 
			
		||||
    def test_enqueue_notifications(self) -> None:
 | 
			
		||||
        # Boring message doesn't send a notice
 | 
			
		||||
        email_notice, mobile_notice = self.check_will_notify()
 | 
			
		||||
        self.assertTrue(email_notice is None)
 | 
			
		||||
        self.assertTrue(mobile_notice is None)
 | 
			
		||||
 | 
			
		||||
        # Private message sends a notice
 | 
			
		||||
        email_notice, mobile_notice = self.check_will_notify(private_message=True)
 | 
			
		||||
        self.assertTrue(email_notice is not None)
 | 
			
		||||
        self.assertTrue(mobile_notice is not None)
 | 
			
		||||
 | 
			
		||||
        # Private message won't double-send either notice if we've
 | 
			
		||||
        # already sent notices before.
 | 
			
		||||
        email_notice, mobile_notice = self.check_will_notify(
 | 
			
		||||
            private_message=True, already_notified={"push_notified": True, "email_notified": False}
 | 
			
		||||
        )
 | 
			
		||||
        self.assertTrue(email_notice is not None)
 | 
			
		||||
        self.assertTrue(mobile_notice is None)
 | 
			
		||||
 | 
			
		||||
        email_notice, mobile_notice = self.check_will_notify(
 | 
			
		||||
            private_message=True, already_notified={"push_notified": False, "email_notified": True}
 | 
			
		||||
        )
 | 
			
		||||
        self.assertTrue(email_notice is None)
 | 
			
		||||
        self.assertTrue(mobile_notice is not None)
 | 
			
		||||
 | 
			
		||||
        # Mention sends a notice
 | 
			
		||||
        email_notice, mobile_notice = self.check_will_notify(mentioned=True)
 | 
			
		||||
        self.assertTrue(email_notice is not None)
 | 
			
		||||
        self.assertTrue(mobile_notice is not None)
 | 
			
		||||
 | 
			
		||||
        # Wildcard mention triggers both email and push notices (Like a
 | 
			
		||||
        # direct mention, whether the notice is actually delivered is
 | 
			
		||||
        # determined later, in the email/push notification code)
 | 
			
		||||
        email_notice, mobile_notice = self.check_will_notify(wildcard_mention_notify=True)
 | 
			
		||||
        self.assertTrue(email_notice is not None)
 | 
			
		||||
        self.assertTrue(mobile_notice is not None)
 | 
			
		||||
 | 
			
		||||
        # stream_push_notify pushes but doesn't email
 | 
			
		||||
        email_notice, mobile_notice = self.check_will_notify(
 | 
			
		||||
            stream_push_notify=True, stream_name="Denmark"
 | 
			
		||||
        )
 | 
			
		||||
        self.assertTrue(email_notice is None)
 | 
			
		||||
        self.assertTrue(mobile_notice is not None)
 | 
			
		||||
 | 
			
		||||
        # stream_email_notify emails but doesn't push
 | 
			
		||||
        email_notice, mobile_notice = self.check_will_notify(
 | 
			
		||||
            stream_email_notify=True, stream_name="Denmark"
 | 
			
		||||
        )
 | 
			
		||||
        self.assertTrue(email_notice is not None)
 | 
			
		||||
        self.assertTrue(mobile_notice is None)
 | 
			
		||||
 | 
			
		||||
        # Private message doesn't send a notice if not idle
 | 
			
		||||
        email_notice, mobile_notice = self.check_will_notify(private_message=True, idle=False)
 | 
			
		||||
        self.assertTrue(email_notice is None)
 | 
			
		||||
        self.assertTrue(mobile_notice is None)
 | 
			
		||||
 | 
			
		||||
        # Mention doesn't send a notice if not idle
 | 
			
		||||
        email_notice, mobile_notice = self.check_will_notify(mentioned=True, idle=False)
 | 
			
		||||
        self.assertTrue(email_notice is None)
 | 
			
		||||
        self.assertTrue(mobile_notice is None)
 | 
			
		||||
 | 
			
		||||
        # Wildcard mention doesn't send a notice if not idle
 | 
			
		||||
        email_notice, mobile_notice = self.check_will_notify(
 | 
			
		||||
            wildcard_mention_notify=True, idle=False
 | 
			
		||||
        )
 | 
			
		||||
        self.assertTrue(email_notice is None)
 | 
			
		||||
        self.assertTrue(mobile_notice is None)
 | 
			
		||||
 | 
			
		||||
        # Private message sends push but not email if not idle but online_push_enabled
 | 
			
		||||
        email_notice, mobile_notice = self.check_will_notify(
 | 
			
		||||
            private_message=True, online_push_enabled=True, idle=False
 | 
			
		||||
        )
 | 
			
		||||
        self.assertTrue(email_notice is None)
 | 
			
		||||
        self.assertTrue(mobile_notice is not None)
 | 
			
		||||
 | 
			
		||||
        # Stream message sends push (if enabled for that stream) but not email if not
 | 
			
		||||
        # idle but online_push_enabled
 | 
			
		||||
        email_notice, mobile_notice = self.check_will_notify(
 | 
			
		||||
            stream_push_notify=True,
 | 
			
		||||
            stream_email_notify=True,
 | 
			
		||||
            stream_name="Denmark",
 | 
			
		||||
            online_push_enabled=True,
 | 
			
		||||
            idle=False,
 | 
			
		||||
            already_notified={},
 | 
			
		||||
        )
 | 
			
		||||
        self.assertTrue(email_notice is None)
 | 
			
		||||
        self.assertTrue(mobile_notice is not None)
 | 
			
		||||
 | 
			
		||||
    def tornado_call(
 | 
			
		||||
        self,
 | 
			
		||||
@@ -246,23 +155,14 @@ class MissedMessageNotificationsTest(ZulipTestCase):
 | 
			
		||||
 | 
			
		||||
        def assert_maybe_enqueue_notifications_call_args(
 | 
			
		||||
            args_dict: Collection[Any],
 | 
			
		||||
            user_profile_id: int,
 | 
			
		||||
            message_id: int,
 | 
			
		||||
            **kwargs: Union[bool, Dict[str, bool], Optional[str]],
 | 
			
		||||
            **kwargs: Any,
 | 
			
		||||
        ) -> None:
 | 
			
		||||
            kwargs = self.get_maybe_enqueue_notifications_parameters(**kwargs)
 | 
			
		||||
            expected_args_dict = dict(
 | 
			
		||||
                user_profile_id=user_profile_id,
 | 
			
		||||
            expected_args_dict = self.get_maybe_enqueue_notifications_parameters(
 | 
			
		||||
                user_id=user_profile.id,
 | 
			
		||||
                sender_id=iago.id,
 | 
			
		||||
                message_id=message_id,
 | 
			
		||||
                private_message=kwargs["private_message"],
 | 
			
		||||
                mentioned=kwargs["mentioned"],
 | 
			
		||||
                wildcard_mention_notify=kwargs["wildcard_mention_notify"],
 | 
			
		||||
                stream_push_notify=kwargs["stream_push_notify"],
 | 
			
		||||
                stream_email_notify=kwargs["stream_email_notify"],
 | 
			
		||||
                stream_name=kwargs["stream_name"],
 | 
			
		||||
                online_push_enabled=kwargs["online_push_enabled"],
 | 
			
		||||
                idle=kwargs["idle"],
 | 
			
		||||
                already_notified=kwargs["already_notified"],
 | 
			
		||||
                **kwargs,
 | 
			
		||||
            )
 | 
			
		||||
            self.assertEqual(args_dict, expected_args_dict)
 | 
			
		||||
 | 
			
		||||
@@ -285,7 +185,6 @@ class MissedMessageNotificationsTest(ZulipTestCase):
 | 
			
		||||
 | 
			
		||||
            assert_maybe_enqueue_notifications_call_args(
 | 
			
		||||
                args_dict=args_dict,
 | 
			
		||||
                user_profile_id=user_profile.id,
 | 
			
		||||
                message_id=msg_id,
 | 
			
		||||
                stream_name="Denmark",
 | 
			
		||||
                already_notified={"email_notified": False, "push_notified": False},
 | 
			
		||||
@@ -303,7 +202,6 @@ class MissedMessageNotificationsTest(ZulipTestCase):
 | 
			
		||||
 | 
			
		||||
            assert_maybe_enqueue_notifications_call_args(
 | 
			
		||||
                args_dict=args_dict,
 | 
			
		||||
                user_profile_id=user_profile.id,
 | 
			
		||||
                message_id=msg_id,
 | 
			
		||||
                private_message=True,
 | 
			
		||||
                already_notified={"email_notified": True, "push_notified": True},
 | 
			
		||||
@@ -323,8 +221,8 @@ class MissedMessageNotificationsTest(ZulipTestCase):
 | 
			
		||||
 | 
			
		||||
            assert_maybe_enqueue_notifications_call_args(
 | 
			
		||||
                args_dict=args_dict,
 | 
			
		||||
                user_profile_id=user_profile.id,
 | 
			
		||||
                message_id=msg_id,
 | 
			
		||||
                flags=["mentioned"],
 | 
			
		||||
                mentioned=True,
 | 
			
		||||
                stream_name="Denmark",
 | 
			
		||||
                already_notified={"email_notified": True, "push_notified": True},
 | 
			
		||||
@@ -342,8 +240,8 @@ class MissedMessageNotificationsTest(ZulipTestCase):
 | 
			
		||||
 | 
			
		||||
            assert_maybe_enqueue_notifications_call_args(
 | 
			
		||||
                args_dict=args_dict,
 | 
			
		||||
                user_profile_id=user_profile.id,
 | 
			
		||||
                message_id=msg_id,
 | 
			
		||||
                flags=["wildcard_mentioned"],
 | 
			
		||||
                wildcard_mention_notify=True,
 | 
			
		||||
                stream_name="Denmark",
 | 
			
		||||
                already_notified={"email_notified": True, "push_notified": True},
 | 
			
		||||
@@ -362,7 +260,7 @@ class MissedMessageNotificationsTest(ZulipTestCase):
 | 
			
		||||
 | 
			
		||||
            assert_maybe_enqueue_notifications_call_args(
 | 
			
		||||
                args_dict=args_dict,
 | 
			
		||||
                user_profile_id=user_profile.id,
 | 
			
		||||
                flags=["wildcard_mentioned"],
 | 
			
		||||
                message_id=msg_id,
 | 
			
		||||
                stream_name="Denmark",
 | 
			
		||||
                already_notified={"email_notified": False, "push_notified": False},
 | 
			
		||||
@@ -383,8 +281,8 @@ class MissedMessageNotificationsTest(ZulipTestCase):
 | 
			
		||||
 | 
			
		||||
            assert_maybe_enqueue_notifications_call_args(
 | 
			
		||||
                args_dict=args_dict,
 | 
			
		||||
                user_profile_id=user_profile.id,
 | 
			
		||||
                message_id=msg_id,
 | 
			
		||||
                flags=["wildcard_mentioned"],
 | 
			
		||||
                stream_name="Denmark",
 | 
			
		||||
                already_notified={"email_notified": False, "push_notified": False},
 | 
			
		||||
            )
 | 
			
		||||
@@ -408,8 +306,8 @@ class MissedMessageNotificationsTest(ZulipTestCase):
 | 
			
		||||
 | 
			
		||||
            assert_maybe_enqueue_notifications_call_args(
 | 
			
		||||
                args_dict=args_dict,
 | 
			
		||||
                user_profile_id=user_profile.id,
 | 
			
		||||
                message_id=msg_id,
 | 
			
		||||
                flags=["wildcard_mentioned"],
 | 
			
		||||
                wildcard_mention_notify=True,
 | 
			
		||||
                stream_name="Denmark",
 | 
			
		||||
                already_notified={"email_notified": True, "push_notified": True},
 | 
			
		||||
@@ -432,7 +330,6 @@ class MissedMessageNotificationsTest(ZulipTestCase):
 | 
			
		||||
 | 
			
		||||
            assert_maybe_enqueue_notifications_call_args(
 | 
			
		||||
                args_dict=args_dict,
 | 
			
		||||
                user_profile_id=user_profile.id,
 | 
			
		||||
                message_id=msg_id,
 | 
			
		||||
                stream_push_notify=True,
 | 
			
		||||
                stream_email_notify=False,
 | 
			
		||||
@@ -455,7 +352,6 @@ class MissedMessageNotificationsTest(ZulipTestCase):
 | 
			
		||||
 | 
			
		||||
            assert_maybe_enqueue_notifications_call_args(
 | 
			
		||||
                args_dict=args_dict,
 | 
			
		||||
                user_profile_id=user_profile.id,
 | 
			
		||||
                message_id=msg_id,
 | 
			
		||||
                stream_push_notify=False,
 | 
			
		||||
                stream_email_notify=True,
 | 
			
		||||
@@ -486,7 +382,6 @@ class MissedMessageNotificationsTest(ZulipTestCase):
 | 
			
		||||
 | 
			
		||||
            assert_maybe_enqueue_notifications_call_args(
 | 
			
		||||
                args_dict=args_dict,
 | 
			
		||||
                user_profile_id=user_profile.id,
 | 
			
		||||
                message_id=msg_id,
 | 
			
		||||
                stream_name="Denmark",
 | 
			
		||||
                already_notified={"email_notified": False, "push_notified": False},
 | 
			
		||||
@@ -510,7 +405,6 @@ class MissedMessageNotificationsTest(ZulipTestCase):
 | 
			
		||||
 | 
			
		||||
            assert_maybe_enqueue_notifications_call_args(
 | 
			
		||||
                args_dict=args_dict,
 | 
			
		||||
                user_profile_id=user_profile.id,
 | 
			
		||||
                message_id=msg_id,
 | 
			
		||||
                stream_name="Denmark",
 | 
			
		||||
                already_notified={"email_notified": False, "push_notified": False},
 | 
			
		||||
@@ -530,8 +424,17 @@ class MissedMessageNotificationsTest(ZulipTestCase):
 | 
			
		||||
        msg_id = self.send_personal_message(iago, user_profile)
 | 
			
		||||
        with mock.patch("zerver.tornado.event_queue.maybe_enqueue_notifications") as mock_enqueue:
 | 
			
		||||
            missedmessage_hook(user_profile.id, client_descriptor, True)
 | 
			
		||||
            # If the sender is muted, we don't call `maybe_enqueue_notifications` at all.
 | 
			
		||||
            mock_enqueue.assert_not_called()
 | 
			
		||||
            mock_enqueue.assert_called_once()
 | 
			
		||||
            args_dict = mock_enqueue.call_args_list[0][1]
 | 
			
		||||
 | 
			
		||||
            assert_maybe_enqueue_notifications_call_args(
 | 
			
		||||
                args_dict=args_dict,
 | 
			
		||||
                message_id=msg_id,
 | 
			
		||||
                private_message=True,
 | 
			
		||||
                sender_is_muted=True,
 | 
			
		||||
                flags=["read"],
 | 
			
		||||
                already_notified={"email_notified": False, "push_notified": False},
 | 
			
		||||
            )
 | 
			
		||||
        destroy_event_queue(client_descriptor.event_queue.id)
 | 
			
		||||
        result = self.api_delete(user_profile, f"/api/v1/users/me/muted_users/{iago.id}")
 | 
			
		||||
        self.assert_json_success(result)
 | 
			
		||||
 
 | 
			
		||||
@@ -105,7 +105,7 @@ class EditMessageSideEffectsTest(ZulipTestCase):
 | 
			
		||||
        cordelia_calls = [
 | 
			
		||||
            call_args
 | 
			
		||||
            for call_args in m.call_args_list
 | 
			
		||||
            if call_args[1]["user_profile_id"] == cordelia.id
 | 
			
		||||
            if call_args[1]["user_data"].user_id == cordelia.id
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
        if expect_short_circuit:
 | 
			
		||||
@@ -184,10 +184,13 @@ class EditMessageSideEffectsTest(ZulipTestCase):
 | 
			
		||||
        info = notification_message_data["info"]
 | 
			
		||||
 | 
			
		||||
        cordelia = self.example_user("cordelia")
 | 
			
		||||
        hamlet = self.example_user("hamlet")
 | 
			
		||||
        expected_enqueue_kwargs = self.get_maybe_enqueue_notifications_parameters(
 | 
			
		||||
            user_profile_id=cordelia.id,
 | 
			
		||||
            user_id=cordelia.id,
 | 
			
		||||
            sender_id=hamlet.id,
 | 
			
		||||
            message_id=message_id,
 | 
			
		||||
            mentioned=True,
 | 
			
		||||
            flags=["mentioned"],
 | 
			
		||||
            stream_name="Scotland",
 | 
			
		||||
            already_notified={},
 | 
			
		||||
        )
 | 
			
		||||
@@ -295,6 +298,7 @@ class EditMessageSideEffectsTest(ZulipTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_online_push_enabled_for_fully_present_mentioned_user(self) -> None:
 | 
			
		||||
        cordelia = self.example_user("cordelia")
 | 
			
		||||
        hamlet = self.example_user("hamlet")
 | 
			
		||||
 | 
			
		||||
        # Simulate Cordelia is FULLY present, not just in term of
 | 
			
		||||
        # browser activity, but also in terms of her client descriptors.
 | 
			
		||||
@@ -312,10 +316,12 @@ class EditMessageSideEffectsTest(ZulipTestCase):
 | 
			
		||||
        info = notification_message_data["info"]
 | 
			
		||||
 | 
			
		||||
        expected_enqueue_kwargs = self.get_maybe_enqueue_notifications_parameters(
 | 
			
		||||
            user_profile_id=cordelia.id,
 | 
			
		||||
            user_id=cordelia.id,
 | 
			
		||||
            sender_id=hamlet.id,
 | 
			
		||||
            message_id=message_id,
 | 
			
		||||
            mentioned=True,
 | 
			
		||||
            stream_name="Scotland",
 | 
			
		||||
            flags=["mentioned"],
 | 
			
		||||
            online_push_enabled=True,
 | 
			
		||||
            idle=False,
 | 
			
		||||
            already_notified={},
 | 
			
		||||
@@ -329,6 +335,7 @@ class EditMessageSideEffectsTest(ZulipTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_online_push_enabled_for_fully_present_boring_user(self) -> None:
 | 
			
		||||
        cordelia = self.example_user("cordelia")
 | 
			
		||||
        hamlet = self.example_user("hamlet")
 | 
			
		||||
 | 
			
		||||
        # Simulate Cordelia is FULLY present, not just in term of
 | 
			
		||||
        # browser activity, but also in terms of her client descriptors.
 | 
			
		||||
@@ -346,7 +353,8 @@ class EditMessageSideEffectsTest(ZulipTestCase):
 | 
			
		||||
        info = notification_message_data["info"]
 | 
			
		||||
 | 
			
		||||
        expected_enqueue_kwargs = self.get_maybe_enqueue_notifications_parameters(
 | 
			
		||||
            user_profile_id=cordelia.id,
 | 
			
		||||
            user_id=cordelia.id,
 | 
			
		||||
            sender_id=hamlet.id,
 | 
			
		||||
            message_id=message_id,
 | 
			
		||||
            stream_name="Scotland",
 | 
			
		||||
            online_push_enabled=True,
 | 
			
		||||
@@ -381,9 +389,11 @@ class EditMessageSideEffectsTest(ZulipTestCase):
 | 
			
		||||
        info = notification_message_data["info"]
 | 
			
		||||
 | 
			
		||||
        expected_enqueue_kwargs = self.get_maybe_enqueue_notifications_parameters(
 | 
			
		||||
            user_profile_id=cordelia.id,
 | 
			
		||||
            user_id=cordelia.id,
 | 
			
		||||
            message_id=message_id,
 | 
			
		||||
            sender_id=self.example_user("hamlet").id,
 | 
			
		||||
            mentioned=True,
 | 
			
		||||
            flags=["mentioned"],
 | 
			
		||||
            stream_name="Scotland",
 | 
			
		||||
            already_notified={},
 | 
			
		||||
        )
 | 
			
		||||
@@ -395,6 +405,7 @@ class EditMessageSideEffectsTest(ZulipTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_updates_with_wildcard_mention(self) -> None:
 | 
			
		||||
        cordelia = self.example_user("cordelia")
 | 
			
		||||
        hamlet = self.example_user("hamlet")
 | 
			
		||||
 | 
			
		||||
        # We will simulate that the user still has a an active client,
 | 
			
		||||
        # but they don't have UserPresence rows, so we will still
 | 
			
		||||
@@ -411,9 +422,11 @@ class EditMessageSideEffectsTest(ZulipTestCase):
 | 
			
		||||
        info = notification_message_data["info"]
 | 
			
		||||
 | 
			
		||||
        expected_enqueue_kwargs = self.get_maybe_enqueue_notifications_parameters(
 | 
			
		||||
            user_profile_id=cordelia.id,
 | 
			
		||||
            user_id=cordelia.id,
 | 
			
		||||
            sender_id=hamlet.id,
 | 
			
		||||
            message_id=message_id,
 | 
			
		||||
            wildcard_mention_notify=True,
 | 
			
		||||
            flags=["wildcard_mentioned"],
 | 
			
		||||
            stream_name="Scotland",
 | 
			
		||||
            already_notified={},
 | 
			
		||||
        )
 | 
			
		||||
@@ -450,6 +463,7 @@ class EditMessageSideEffectsTest(ZulipTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_updates_with_stream_mention_of_fully_present_user(self) -> None:
 | 
			
		||||
        cordelia = self.example_user("cordelia")
 | 
			
		||||
        hamlet = self.example_user("hamlet")
 | 
			
		||||
 | 
			
		||||
        # Simulate Cordelia is FULLY present, not just in term of
 | 
			
		||||
        # browser activity, but also in terms of her client descriptors.
 | 
			
		||||
@@ -466,9 +480,11 @@ class EditMessageSideEffectsTest(ZulipTestCase):
 | 
			
		||||
        info = notification_message_data["info"]
 | 
			
		||||
 | 
			
		||||
        expected_enqueue_kwargs = self.get_maybe_enqueue_notifications_parameters(
 | 
			
		||||
            user_profile_id=cordelia.id,
 | 
			
		||||
            user_id=cordelia.id,
 | 
			
		||||
            sender_id=hamlet.id,
 | 
			
		||||
            message_id=message_id,
 | 
			
		||||
            mentioned=True,
 | 
			
		||||
            flags=["mentioned"],
 | 
			
		||||
            stream_name="Scotland",
 | 
			
		||||
            idle=False,
 | 
			
		||||
            already_notified={},
 | 
			
		||||
 
 | 
			
		||||
@@ -312,7 +312,7 @@ class MutedUsersTests(ZulipTestCase):
 | 
			
		||||
            self.assert_json_success(result)
 | 
			
		||||
            m.assert_called_once()
 | 
			
		||||
            # `maybe_enqueue_notificaions` was called for Hamlet after message edit mentioned him.
 | 
			
		||||
            self.assertEqual(m.call_args_list[0][1]["user_profile_id"], hamlet.id)
 | 
			
		||||
            self.assertEqual(m.call_args_list[0][1]["user_data"].user_id, hamlet.id)
 | 
			
		||||
 | 
			
		||||
        # Hamlet mutes Cordelia.
 | 
			
		||||
        self.login("hamlet")
 | 
			
		||||
 
 | 
			
		||||
@@ -737,26 +737,26 @@ def missedmessage_hook(
 | 
			
		||||
        if event["type"] != "message":
 | 
			
		||||
            continue
 | 
			
		||||
        internal_data = event.get("internal_data", {})
 | 
			
		||||
        sender_id = event["message"]["sender_id"]
 | 
			
		||||
 | 
			
		||||
        sender_is_muted = internal_data.get("sender_is_muted", False)
 | 
			
		||||
        if sender_is_muted:
 | 
			
		||||
            continue
 | 
			
		||||
        user_notifications_data = UserMessageNotificationsData(
 | 
			
		||||
            user_id=user_profile_id,
 | 
			
		||||
            flags=event.get("flags", []),
 | 
			
		||||
            sender_is_muted=internal_data.get("sender_is_muted", False),
 | 
			
		||||
            mentioned=internal_data.get("mentioned", False),
 | 
			
		||||
            stream_push_notify=internal_data.get("stream_push_notify", False),
 | 
			
		||||
            stream_email_notify=internal_data.get("stream_email_notify", False),
 | 
			
		||||
            wildcard_mention_notify=internal_data.get("wildcard_mention_notify", False),
 | 
			
		||||
            # Since one is by definition idle, we don't need to check online_push_enabled
 | 
			
		||||
            online_push_enabled=False,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        assert "flags" in event
 | 
			
		||||
 | 
			
		||||
        mentioned = internal_data.get("mentioned", False)
 | 
			
		||||
        private_message = event["message"]["type"] == "private"
 | 
			
		||||
        # stream_push_notify is set in process_message_event.
 | 
			
		||||
        stream_push_notify = internal_data.get("stream_push_notify", False)
 | 
			
		||||
        stream_email_notify = internal_data.get("stream_email_notify", False)
 | 
			
		||||
        wildcard_mention_notify = internal_data.get("wildcard_mention_notify", False)
 | 
			
		||||
 | 
			
		||||
        stream_name = None
 | 
			
		||||
        if not private_message:
 | 
			
		||||
            stream_name = event["message"]["display_recipient"]
 | 
			
		||||
 | 
			
		||||
        # Since one is by definition idle, we don't need to check online_push_enabled
 | 
			
		||||
        online_push_enabled = False
 | 
			
		||||
        # Since we just GC'd the last event queue, the user is definitely idle.
 | 
			
		||||
        idle = True
 | 
			
		||||
 | 
			
		||||
@@ -767,15 +767,11 @@ def missedmessage_hook(
 | 
			
		||||
            email_notified=internal_data.get("email_notified", False),
 | 
			
		||||
        )
 | 
			
		||||
        maybe_enqueue_notifications(
 | 
			
		||||
            user_profile_id=user_profile_id,
 | 
			
		||||
            user_data=user_notifications_data,
 | 
			
		||||
            sender_id=sender_id,
 | 
			
		||||
            message_id=message_id,
 | 
			
		||||
            private_message=private_message,
 | 
			
		||||
            mentioned=mentioned,
 | 
			
		||||
            wildcard_mention_notify=wildcard_mention_notify,
 | 
			
		||||
            stream_push_notify=stream_push_notify,
 | 
			
		||||
            stream_email_notify=stream_email_notify,
 | 
			
		||||
            stream_name=stream_name,
 | 
			
		||||
            online_push_enabled=online_push_enabled,
 | 
			
		||||
            idle=idle,
 | 
			
		||||
            already_notified=already_notified,
 | 
			
		||||
        )
 | 
			
		||||
@@ -794,15 +790,11 @@ def receiver_is_off_zulip(user_profile_id: int) -> bool:
 | 
			
		||||
 | 
			
		||||
def maybe_enqueue_notifications(
 | 
			
		||||
    *,
 | 
			
		||||
    user_profile_id: int,
 | 
			
		||||
    user_data: UserMessageNotificationsData,
 | 
			
		||||
    sender_id: int,
 | 
			
		||||
    message_id: int,
 | 
			
		||||
    private_message: bool,
 | 
			
		||||
    mentioned: bool,
 | 
			
		||||
    wildcard_mention_notify: bool,
 | 
			
		||||
    stream_push_notify: bool,
 | 
			
		||||
    stream_email_notify: bool,
 | 
			
		||||
    stream_name: Optional[str],
 | 
			
		||||
    online_push_enabled: bool,
 | 
			
		||||
    idle: bool,
 | 
			
		||||
    already_notified: Dict[str, bool],
 | 
			
		||||
) -> Dict[str, bool]:
 | 
			
		||||
@@ -815,17 +807,15 @@ def maybe_enqueue_notifications(
 | 
			
		||||
    """
 | 
			
		||||
    notified: Dict[str, bool] = {}
 | 
			
		||||
 | 
			
		||||
    if (idle or online_push_enabled) and (
 | 
			
		||||
        private_message or mentioned or wildcard_mention_notify or stream_push_notify
 | 
			
		||||
    ):
 | 
			
		||||
        notice = build_offline_notification(user_profile_id, message_id)
 | 
			
		||||
    if user_data.is_push_notifiable(private_message, sender_id, idle):
 | 
			
		||||
        notice = build_offline_notification(user_data.user_id, message_id)
 | 
			
		||||
        if private_message:
 | 
			
		||||
            notice["trigger"] = "private_message"
 | 
			
		||||
        elif mentioned:
 | 
			
		||||
        elif user_data.mentioned:
 | 
			
		||||
            notice["trigger"] = "mentioned"
 | 
			
		||||
        elif wildcard_mention_notify:
 | 
			
		||||
        elif user_data.wildcard_mention_notify:
 | 
			
		||||
            notice["trigger"] = "wildcard_mentioned"
 | 
			
		||||
        elif stream_push_notify:
 | 
			
		||||
        elif user_data.stream_push_notify:
 | 
			
		||||
            notice["trigger"] = "stream_push_notify"
 | 
			
		||||
        else:
 | 
			
		||||
            raise AssertionError("Unknown notification trigger!")
 | 
			
		||||
@@ -838,15 +828,15 @@ def maybe_enqueue_notifications(
 | 
			
		||||
    # mention.  Eventually, we'll add settings to allow email
 | 
			
		||||
    # notifications to match the model of push notifications
 | 
			
		||||
    # above.
 | 
			
		||||
    if idle and (private_message or mentioned or wildcard_mention_notify or stream_email_notify):
 | 
			
		||||
        notice = build_offline_notification(user_profile_id, message_id)
 | 
			
		||||
    if user_data.is_email_notifiable(private_message, sender_id, idle):
 | 
			
		||||
        notice = build_offline_notification(user_data.user_id, message_id)
 | 
			
		||||
        if private_message:
 | 
			
		||||
            notice["trigger"] = "private_message"
 | 
			
		||||
        elif mentioned:
 | 
			
		||||
        elif user_data.mentioned:
 | 
			
		||||
            notice["trigger"] = "mentioned"
 | 
			
		||||
        elif wildcard_mention_notify:
 | 
			
		||||
        elif user_data.wildcard_mention_notify:
 | 
			
		||||
            notice["trigger"] = "wildcard_mentioned"
 | 
			
		||||
        elif stream_email_notify:
 | 
			
		||||
        elif user_data.stream_email_notify:
 | 
			
		||||
            notice["trigger"] = "stream_email_notify"
 | 
			
		||||
        else:
 | 
			
		||||
            raise AssertionError("Unknown notification trigger!")
 | 
			
		||||
@@ -966,8 +956,8 @@ def process_message_event(
 | 
			
		||||
            muted_sender_user_ids=muted_sender_user_ids,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        # Remove fields sent through other pipes to save some space.
 | 
			
		||||
        internal_data = asdict(user_notifications_data)
 | 
			
		||||
        # Remove fields sent through other pipes to save some space.
 | 
			
		||||
        internal_data.pop("flags")
 | 
			
		||||
        internal_data.pop("user_id")
 | 
			
		||||
        extra_user_data[user_profile_id] = dict(internal_data=internal_data)
 | 
			
		||||
@@ -985,15 +975,11 @@ def process_message_event(
 | 
			
		||||
 | 
			
		||||
        extra_user_data[user_profile_id]["internal_data"].update(
 | 
			
		||||
            maybe_enqueue_notifications(
 | 
			
		||||
                user_profile_id=user_profile_id,
 | 
			
		||||
                user_data=user_notifications_data,
 | 
			
		||||
                sender_id=sender_id,
 | 
			
		||||
                message_id=message_id,
 | 
			
		||||
                private_message=private_message,
 | 
			
		||||
                mentioned=user_notifications_data.mentioned,
 | 
			
		||||
                wildcard_mention_notify=user_notifications_data.wildcard_mention_notify,
 | 
			
		||||
                stream_push_notify=user_notifications_data.stream_push_notify,
 | 
			
		||||
                stream_email_notify=user_notifications_data.stream_email_notify,
 | 
			
		||||
                stream_name=stream_name,
 | 
			
		||||
                online_push_enabled=user_notifications_data.online_push_enabled,
 | 
			
		||||
                idle=idle,
 | 
			
		||||
                already_notified={},
 | 
			
		||||
            )
 | 
			
		||||
@@ -1126,6 +1112,25 @@ def process_message_update_event(
 | 
			
		||||
 | 
			
		||||
    for user_data in users:
 | 
			
		||||
        user_profile_id = user_data["id"]
 | 
			
		||||
 | 
			
		||||
        if "user_id" in event_template:
 | 
			
		||||
            # This is inaccurate: the user we'll get here will be the
 | 
			
		||||
            # sender if the message's content was edited, which is
 | 
			
		||||
            # typically where we might send new notifications.
 | 
			
		||||
            # However, for topic/stream edits, it could be another
 | 
			
		||||
            # user.  We may need to adjust the format for
 | 
			
		||||
            # update_message events to address this issue.
 | 
			
		||||
            message_sender_id = event_template["user_id"]
 | 
			
		||||
        else:
 | 
			
		||||
            # This is also inaccurate, but usefully so.  Events
 | 
			
		||||
            # without a `user_id` field come from the
 | 
			
		||||
            # do_update_embedded_data code path, and represent not an
 | 
			
		||||
            # update to the raw content, but instead just rendering
 | 
			
		||||
            # previews. Setting the current user at the sender is a
 | 
			
		||||
            # hack to simplify notifications logic for this code
 | 
			
		||||
            # path. TODO: Change this to short-circuit more directly.
 | 
			
		||||
            message_sender_id = user_profile_id
 | 
			
		||||
 | 
			
		||||
        user_event = dict(event_template)  # shallow copy, but deep enough for our needs
 | 
			
		||||
        for key in user_data.keys():
 | 
			
		||||
            if key != "id":
 | 
			
		||||
@@ -1145,6 +1150,7 @@ def process_message_update_event(
 | 
			
		||||
        maybe_enqueue_notifications_for_message_update(
 | 
			
		||||
            user_data=user_notifications_data,
 | 
			
		||||
            message_id=message_id,
 | 
			
		||||
            sender_id=message_sender_id,
 | 
			
		||||
            private_message=(stream_name is None),
 | 
			
		||||
            stream_name=stream_name,
 | 
			
		||||
            presence_idle=(user_profile_id in presence_idle_user_ids),
 | 
			
		||||
@@ -1161,6 +1167,7 @@ def process_message_update_event(
 | 
			
		||||
def maybe_enqueue_notifications_for_message_update(
 | 
			
		||||
    user_data: UserMessageNotificationsData,
 | 
			
		||||
    message_id: int,
 | 
			
		||||
    sender_id: int,
 | 
			
		||||
    private_message: bool,
 | 
			
		||||
    stream_name: Optional[str],
 | 
			
		||||
    presence_idle: bool,
 | 
			
		||||
@@ -1202,15 +1209,11 @@ def maybe_enqueue_notifications_for_message_update(
 | 
			
		||||
    idle = presence_idle or receiver_is_off_zulip(user_data.user_id)
 | 
			
		||||
 | 
			
		||||
    maybe_enqueue_notifications(
 | 
			
		||||
        user_profile_id=user_data.user_id,
 | 
			
		||||
        user_data=user_data,
 | 
			
		||||
        message_id=message_id,
 | 
			
		||||
        sender_id=sender_id,
 | 
			
		||||
        private_message=private_message,
 | 
			
		||||
        mentioned=user_data.mentioned,
 | 
			
		||||
        wildcard_mention_notify=user_data.wildcard_mention_notify,
 | 
			
		||||
        stream_push_notify=user_data.stream_push_notify,
 | 
			
		||||
        stream_email_notify=user_data.stream_email_notify,
 | 
			
		||||
        stream_name=stream_name,
 | 
			
		||||
        online_push_enabled=user_data.online_push_enabled,
 | 
			
		||||
        idle=idle,
 | 
			
		||||
        already_notified={},
 | 
			
		||||
    )
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user