mirror of
https://github.com/zulip/zulip.git
synced 2025-11-01 20:44:04 +00:00
push_notifications: Skip enqueuing notif for users without push devices.
Previously, we enqueued events to "missedmessage_mobile_notifications" even for users who had no registered push devices. 'handle_push_notification' later used to perform the check & skip if there were no registered devices. This commit avoids putting such events into the "missedmessage_mobile_notifications" queue at all. By doing so, we reduce unnecessary churn.
This commit is contained in:
committed by
Tim Abbott
parent
aa317ee844
commit
421637ce31
@@ -570,6 +570,7 @@ def update_message_content(
|
||||
event["prior_mention_user_ids"] = list(prior_mention_user_ids)
|
||||
event["presence_idle_user_ids"] = filter_presence_idle_user_ids(info.active_user_ids)
|
||||
event["all_bot_user_ids"] = list(info.all_bot_user_ids)
|
||||
event["push_device_registered_user_ids"] = list(info.push_device_registered_user_ids)
|
||||
if rendering_result.mentions_stream_wildcard:
|
||||
event["stream_wildcard_mention_user_ids"] = list(info.stream_wildcard_mention_user_ids)
|
||||
event["stream_wildcard_mention_in_followed_topic_user_ids"] = list(
|
||||
|
||||
@@ -5,17 +5,18 @@ from collections.abc import Set as AbstractSet
|
||||
from dataclasses import dataclass
|
||||
from datetime import timedelta
|
||||
from email.headerregistry import Address
|
||||
from typing import Any, TypedDict
|
||||
from typing import Any, TypedDict, cast
|
||||
|
||||
import orjson
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import IntegrityError, transaction
|
||||
from django.db.models import F, Q, QuerySet
|
||||
from django.db.models import Exists, F, OuterRef, Q, QuerySet
|
||||
from django.utils.html import escape
|
||||
from django.utils.timezone import now as timezone_now
|
||||
from django.utils.translation import gettext as _
|
||||
from django.utils.translation import override as override_language
|
||||
from django_stubs_ext import WithAnnotations
|
||||
|
||||
from zerver.actions.uploads import do_claim_attachments
|
||||
from zerver.actions.user_topics import (
|
||||
@@ -99,6 +100,8 @@ from zerver.lib.widget import do_widget_post_save_actions
|
||||
from zerver.models import (
|
||||
Client,
|
||||
Message,
|
||||
PushDevice,
|
||||
PushDeviceToken,
|
||||
Realm,
|
||||
Recipient,
|
||||
Stream,
|
||||
@@ -208,6 +211,7 @@ class RecipientInfoResult:
|
||||
all_bot_user_ids: set[int]
|
||||
topic_participant_user_ids: set[int]
|
||||
sender_muted_stream: bool | None
|
||||
push_device_registered_user_ids: set[int]
|
||||
|
||||
|
||||
class ActiveUserDict(TypedDict):
|
||||
@@ -218,6 +222,11 @@ class ActiveUserDict(TypedDict):
|
||||
long_term_idle: bool
|
||||
is_bot: bool
|
||||
bot_type: int | None
|
||||
has_push_device_registered: bool
|
||||
|
||||
|
||||
class UserProfileAnnotations(TypedDict):
|
||||
has_push_device_registered: bool
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -400,16 +409,40 @@ def get_recipient_info(
|
||||
user_ids = message_to_user_id_set | possibly_mentioned_user_ids
|
||||
|
||||
if user_ids:
|
||||
query: QuerySet[UserProfile, ActiveUserDict] = UserProfile.objects.filter(
|
||||
is_active=True
|
||||
).values(
|
||||
"id",
|
||||
"enable_online_push_notifications",
|
||||
"enable_offline_email_notifications",
|
||||
"enable_offline_push_notifications",
|
||||
"is_bot",
|
||||
"bot_type",
|
||||
"long_term_idle",
|
||||
query: QuerySet[WithAnnotations[UserProfile, UserProfileAnnotations], ActiveUserDict] = (
|
||||
# We use 'cast' because django-stubs says:
|
||||
# Currently, the mypy plugin can recognize that specific names were passed to `QuerySet.annotate`
|
||||
# and include them in the type, but does not record the types of these attributes.
|
||||
#
|
||||
# The knowledge of the specific annotated fields is not yet used in creating more specific types
|
||||
# for `QuerySet`'s `values` method, however knowledge that the model was annotated _is_ used to
|
||||
# create a broader type result type for `values`, and to allow `filter`ing on any field.
|
||||
#
|
||||
# ref: https://github.com/typeddjango/django-stubs/blob/de8e0b4dd6588129f0e02f601a71486f42799533/README.md?plain=1#L263
|
||||
cast(
|
||||
"QuerySet[WithAnnotations[UserProfile, UserProfileAnnotations], ActiveUserDict]",
|
||||
UserProfile.objects.filter(is_active=True)
|
||||
.annotate(
|
||||
# Uses index "zerver_pushdevice_user_bouncer_device_id_idx"
|
||||
# and "zerver_pushdevicetoken_user_id_015e5dc1".
|
||||
has_push_device_registered=Exists(
|
||||
PushDevice.objects.filter(
|
||||
user_id=OuterRef("id"), bouncer_device_id__isnull=False
|
||||
)
|
||||
)
|
||||
| Exists(PushDeviceToken.objects.filter(user_id=OuterRef("id")))
|
||||
)
|
||||
.values(
|
||||
"id",
|
||||
"enable_online_push_notifications",
|
||||
"enable_offline_email_notifications",
|
||||
"enable_offline_push_notifications",
|
||||
"is_bot",
|
||||
"bot_type",
|
||||
"long_term_idle",
|
||||
"has_push_device_registered",
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
# query_for_ids is fast highly optimized for large queries, and we
|
||||
@@ -487,6 +520,9 @@ def get_recipient_info(
|
||||
# where we determine notifiability of the message for users.
|
||||
all_bot_user_ids = {row["id"] for row in rows if row["is_bot"]}
|
||||
|
||||
# Users who have at least one push device registered to receive push notifications.
|
||||
push_device_registered_user_ids = get_ids_for(lambda r: r["has_push_device_registered"])
|
||||
|
||||
return RecipientInfoResult(
|
||||
active_user_ids=active_user_ids,
|
||||
online_push_user_ids=online_push_user_ids,
|
||||
@@ -508,6 +544,7 @@ def get_recipient_info(
|
||||
all_bot_user_ids=all_bot_user_ids,
|
||||
topic_participant_user_ids=topic_participant_user_ids,
|
||||
sender_muted_stream=sender_muted_stream,
|
||||
push_device_registered_user_ids=push_device_registered_user_ids,
|
||||
)
|
||||
|
||||
|
||||
@@ -712,6 +749,7 @@ def build_message_send_dict(
|
||||
default_bot_user_ids=info.default_bot_user_ids,
|
||||
service_bot_tuples=info.service_bot_tuples,
|
||||
all_bot_user_ids=info.all_bot_user_ids,
|
||||
push_device_registered_user_ids=info.push_device_registered_user_ids,
|
||||
topic_wildcard_mention_user_ids=topic_wildcard_mention_user_ids,
|
||||
stream_wildcard_mention_user_ids=stream_wildcard_mention_user_ids,
|
||||
topic_wildcard_mention_in_followed_topic_user_ids=topic_wildcard_mention_in_followed_topic_user_ids,
|
||||
@@ -1124,6 +1162,7 @@ def do_send_messages(
|
||||
stream_wildcard_mention_in_followed_topic_user_ids=send_request.stream_wildcard_mention_in_followed_topic_user_ids,
|
||||
muted_sender_user_ids=send_request.muted_sender_user_ids,
|
||||
all_bot_user_ids=send_request.all_bot_user_ids,
|
||||
push_device_registered_user_ids=send_request.push_device_registered_user_ids,
|
||||
)
|
||||
for user_id in send_request.active_user_ids
|
||||
]
|
||||
@@ -1167,6 +1206,7 @@ def do_send_messages(
|
||||
),
|
||||
muted_sender_user_ids=list(send_request.muted_sender_user_ids),
|
||||
all_bot_user_ids=list(send_request.all_bot_user_ids),
|
||||
push_device_registered_user_ids=list(send_request.push_device_registered_user_ids),
|
||||
disable_external_notifications=send_request.disable_external_notifications,
|
||||
realm_host=send_request.realm.host,
|
||||
)
|
||||
|
||||
@@ -149,6 +149,7 @@ class SendMessageRequest:
|
||||
default_bot_user_ids: set[int]
|
||||
service_bot_tuples: list[tuple[int, int]]
|
||||
all_bot_user_ids: set[int]
|
||||
push_device_registered_user_ids: set[int]
|
||||
# IDs of topic participants who should be notified of topic wildcard mention.
|
||||
# The 'user_allows_notifications_in_StreamTopic' with 'wildcard_mentions_notify'
|
||||
# setting ON should return True.
|
||||
|
||||
@@ -71,6 +71,7 @@ class UserMessageNotificationsData:
|
||||
stream_wildcard_mention_in_followed_topic_user_ids: set[int],
|
||||
muted_sender_user_ids: set[int],
|
||||
all_bot_user_ids: set[int],
|
||||
push_device_registered_user_ids: set[int],
|
||||
) -> "UserMessageNotificationsData":
|
||||
if user_id in all_bot_user_ids:
|
||||
# Don't send any notifications to bots
|
||||
@@ -126,30 +127,46 @@ class UserMessageNotificationsData:
|
||||
and "stream_wildcard_mentioned" in flags
|
||||
)
|
||||
|
||||
dm_push_notify = user_id not in dm_mention_push_disabled_user_ids and private_message
|
||||
push_device_registered = user_id in push_device_registered_user_ids
|
||||
dm_push_notify = (
|
||||
push_device_registered
|
||||
and user_id not in dm_mention_push_disabled_user_ids
|
||||
and private_message
|
||||
)
|
||||
mention_push_notify = (
|
||||
user_id not in dm_mention_push_disabled_user_ids and "mentioned" in flags
|
||||
push_device_registered
|
||||
and user_id not in dm_mention_push_disabled_user_ids
|
||||
and "mentioned" in flags
|
||||
)
|
||||
topic_wildcard_mention_push_notify = (
|
||||
user_id in topic_wildcard_mention_user_ids
|
||||
push_device_registered
|
||||
and user_id in topic_wildcard_mention_user_ids
|
||||
and user_id not in dm_mention_push_disabled_user_ids
|
||||
and "topic_wildcard_mentioned" in flags
|
||||
)
|
||||
stream_wildcard_mention_push_notify = (
|
||||
user_id in stream_wildcard_mention_user_ids
|
||||
push_device_registered
|
||||
and user_id in stream_wildcard_mention_user_ids
|
||||
and user_id not in dm_mention_push_disabled_user_ids
|
||||
and "stream_wildcard_mentioned" in flags
|
||||
)
|
||||
topic_wildcard_mention_in_followed_topic_push_notify = (
|
||||
user_id in topic_wildcard_mention_in_followed_topic_user_ids
|
||||
push_device_registered
|
||||
and user_id in topic_wildcard_mention_in_followed_topic_user_ids
|
||||
and user_id not in dm_mention_push_disabled_user_ids
|
||||
and "topic_wildcard_mentioned" in flags
|
||||
)
|
||||
stream_wildcard_mention_in_followed_topic_push_notify = (
|
||||
user_id in stream_wildcard_mention_in_followed_topic_user_ids
|
||||
push_device_registered
|
||||
and user_id in stream_wildcard_mention_in_followed_topic_user_ids
|
||||
and user_id not in dm_mention_push_disabled_user_ids
|
||||
and "stream_wildcard_mentioned" in flags
|
||||
)
|
||||
online_push_enabled = push_device_registered and user_id in online_push_user_ids
|
||||
stream_push_notify = push_device_registered and user_id in stream_push_user_ids
|
||||
followed_topic_push_notify = (
|
||||
push_device_registered and user_id in followed_topic_push_user_ids
|
||||
)
|
||||
return cls(
|
||||
user_id=user_id,
|
||||
dm_email_notify=dm_email_notify,
|
||||
@@ -160,10 +177,10 @@ class UserMessageNotificationsData:
|
||||
mention_push_notify=mention_push_notify,
|
||||
topic_wildcard_mention_push_notify=topic_wildcard_mention_push_notify,
|
||||
stream_wildcard_mention_push_notify=stream_wildcard_mention_push_notify,
|
||||
online_push_enabled=user_id in online_push_user_ids,
|
||||
stream_push_notify=user_id in stream_push_user_ids,
|
||||
online_push_enabled=online_push_enabled,
|
||||
stream_push_notify=stream_push_notify,
|
||||
stream_email_notify=user_id in stream_email_user_ids,
|
||||
followed_topic_push_notify=user_id in followed_topic_push_user_ids,
|
||||
followed_topic_push_notify=followed_topic_push_notify,
|
||||
followed_topic_email_notify=user_id in followed_topic_email_user_ids,
|
||||
topic_wildcard_mention_in_followed_topic_push_notify=topic_wildcard_mention_in_followed_topic_push_notify,
|
||||
topic_wildcard_mention_in_followed_topic_email_notify=topic_wildcard_mention_in_followed_topic_email_notify,
|
||||
|
||||
@@ -1917,6 +1917,23 @@ Output:
|
||||
realm, licenses, licenses_at_next_renewal, CustomerPlan.BILLING_SCHEDULE_MONTHLY
|
||||
)
|
||||
|
||||
def register_push_device(self, user_profile_id: int) -> None:
|
||||
PushDevice.objects.create(
|
||||
user_id=user_profile_id,
|
||||
push_account_id=10,
|
||||
bouncer_device_id=1,
|
||||
token_kind=PushDevice.TokenKind.FCM,
|
||||
push_public_key="n4WTVqj8KH6u0vScRycR4TqRaHhFeJ0POvMb8LCu8iI=",
|
||||
)
|
||||
|
||||
def register_push_device_token(self, user_profile_id: int) -> None:
|
||||
PushDeviceToken.objects.create(
|
||||
user_id=user_profile_id,
|
||||
kind=PushDeviceToken.APNS,
|
||||
token="test-token",
|
||||
ios_app_id="com.zulip.flutter",
|
||||
)
|
||||
|
||||
def create_user_notifications_data_object(
|
||||
self, *, user_id: int, **kwargs: Any
|
||||
) -> UserMessageNotificationsData:
|
||||
|
||||
@@ -16,7 +16,7 @@ from zerver.actions.user_topics import do_set_user_topic_visibility_policy
|
||||
from zerver.lib.cache import cache_delete, get_muting_users_cache_key
|
||||
from zerver.lib.test_classes import ZulipTestCase
|
||||
from zerver.lib.test_helpers import HostRequestMock, dummy_handler, mock_queue_publish
|
||||
from zerver.models import Recipient, Subscription, UserProfile, UserTopic
|
||||
from zerver.models import PushDevice, Recipient, Subscription, UserProfile, UserTopic
|
||||
from zerver.models.streams import get_stream
|
||||
from zerver.tornado.event_queue import (
|
||||
ClientDescriptor,
|
||||
@@ -194,6 +194,7 @@ class MissedMessageHookTest(ZulipTestCase):
|
||||
do_change_user_setting(
|
||||
self.user_profile, "enable_online_push_notifications", False, acting_user=None
|
||||
)
|
||||
self.register_push_device(self.user_profile.id)
|
||||
self.iago = self.example_user("iago")
|
||||
self.client_descriptor = self.allocate_event_queue(self.user_profile)
|
||||
self.assertTrue(self.client_descriptor.event_queue.empty())
|
||||
@@ -306,6 +307,28 @@ class MissedMessageHookTest(ZulipTestCase):
|
||||
already_notified={"email_notified": True, "push_notified": False},
|
||||
)
|
||||
|
||||
def test_no_push_device_registered(self) -> None:
|
||||
# When `enable_offline_push_notifications` is `true` but no push device registered,
|
||||
# push notifications should not be sent.
|
||||
do_change_user_setting(
|
||||
self.user_profile, "enable_offline_push_notifications", True, acting_user=None
|
||||
)
|
||||
PushDevice.objects.all().delete()
|
||||
msg_id = self.send_personal_message(self.iago, self.user_profile)
|
||||
with mock.patch("zerver.tornado.event_queue.maybe_enqueue_notifications") as mock_enqueue:
|
||||
missedmessage_hook(self.user_profile.id, self.client_descriptor, True)
|
||||
mock_enqueue.assert_called_once()
|
||||
args_dict = mock_enqueue.call_args_list[0][1]
|
||||
|
||||
self.assert_maybe_enqueue_notifications_call_args(
|
||||
args_dict=args_dict,
|
||||
message_id=msg_id,
|
||||
user_id=self.user_profile.id,
|
||||
dm_email_notify=True,
|
||||
dm_push_notify=False,
|
||||
already_notified={"email_notified": True, "push_notified": False},
|
||||
)
|
||||
|
||||
def test_topic_wildcard_mention(self) -> None:
|
||||
# By default, topic wildcard mentions should send notifications, just like regular mentions
|
||||
self.send_stream_message(self.user_profile, "Denmark")
|
||||
|
||||
@@ -875,6 +875,7 @@ class HandlePushNotificationTest(PushNotificationTestCase):
|
||||
othello = self.example_user("othello")
|
||||
cordelia = self.example_user("cordelia")
|
||||
zulip_realm = get_realm("zulip")
|
||||
self.register_push_device_token(self.user_profile.id)
|
||||
|
||||
# user groups having upto 'MAX_GROUP_SIZE_FOR_MENTION_REACTIVATION'
|
||||
# members are small user groups.
|
||||
|
||||
@@ -3,19 +3,26 @@ from typing import Any
|
||||
from unittest import mock
|
||||
|
||||
from django.utils.timezone import now as timezone_now
|
||||
from typing_extensions import override
|
||||
|
||||
from zerver.actions.user_settings import do_change_user_setting
|
||||
from zerver.actions.user_topics import do_set_user_topic_visibility_policy
|
||||
from zerver.lib.push_notifications import get_apns_badge_count, get_apns_badge_count_future
|
||||
from zerver.lib.test_classes import ZulipTestCase
|
||||
from zerver.lib.test_helpers import mock_queue_publish
|
||||
from zerver.models import Subscription, UserPresence, UserTopic
|
||||
from zerver.models import PushDevice, Subscription, UserPresence, UserTopic
|
||||
from zerver.models.scheduled_jobs import NotificationTriggers
|
||||
from zerver.models.streams import get_stream
|
||||
from zerver.tornado.event_queue import maybe_enqueue_notifications
|
||||
|
||||
|
||||
class EditMessageSideEffectsTest(ZulipTestCase):
|
||||
@override
|
||||
def setUp(self) -> None:
|
||||
super().setUp()
|
||||
cordelia = self.example_user("cordelia")
|
||||
self.register_push_device(cordelia.id)
|
||||
|
||||
def _assert_update_does_not_notify_anybody(self, message_id: int, content: str) -> None:
|
||||
url = "/json/messages/" + str(message_id)
|
||||
|
||||
@@ -223,6 +230,37 @@ class EditMessageSideEffectsTest(ZulipTestCase):
|
||||
updated_content = "re-mention @**Cordelia, Lear's daughter**"
|
||||
self._send_and_update_message(original_content, updated_content, expect_short_circuit=True)
|
||||
|
||||
def test_no_push_device_registered(self) -> None:
|
||||
cordelia = self.example_user("cordelia")
|
||||
hamlet = self.example_user("hamlet")
|
||||
|
||||
# We're interested to test the push notification behaviour when
|
||||
# the user has no push device registered. Disable email notification.
|
||||
do_change_user_setting(
|
||||
cordelia, "enable_offline_email_notifications", False, acting_user=None
|
||||
)
|
||||
self.assertTrue(cordelia.enable_offline_push_notifications)
|
||||
PushDevice.objects.all().delete()
|
||||
|
||||
original_content = "no mention"
|
||||
updated_content = "now we mention @**Cordelia, Lear's daughter**"
|
||||
notification_message_data = self._send_and_update_message(original_content, updated_content)
|
||||
|
||||
message_id = notification_message_data["message_id"]
|
||||
info = notification_message_data["info"]
|
||||
|
||||
expected_enqueue_kwargs = self.get_maybe_enqueue_notifications_parameters(
|
||||
user_id=cordelia.id,
|
||||
acting_user_id=hamlet.id,
|
||||
message_id=message_id,
|
||||
mention_email_notify=False,
|
||||
mention_push_notify=False,
|
||||
already_notified={},
|
||||
)
|
||||
|
||||
self.assertEqual(info["enqueue_kwargs"], expected_enqueue_kwargs)
|
||||
self.assert_length(info["queue_messages"], 0)
|
||||
|
||||
def _turn_on_stream_push_for_cordelia(self) -> None:
|
||||
"""
|
||||
conventions:
|
||||
@@ -632,6 +670,7 @@ class EditMessageSideEffectsTest(ZulipTestCase):
|
||||
self, mock_push_notifications: mock.MagicMock
|
||||
) -> None:
|
||||
mentioned_user = self.example_user("iago")
|
||||
self.register_push_device(mentioned_user.id)
|
||||
self.assertEqual(get_apns_badge_count(mentioned_user), 0)
|
||||
self.assertEqual(get_apns_badge_count_future(mentioned_user), 0)
|
||||
|
||||
@@ -677,6 +716,7 @@ class EditMessageSideEffectsTest(ZulipTestCase):
|
||||
mentioned_user = self.example_user("iago")
|
||||
mentioned_user.enable_stream_push_notifications = True
|
||||
mentioned_user.save()
|
||||
self.register_push_device(mentioned_user.id)
|
||||
|
||||
self.assertEqual(get_apns_badge_count(mentioned_user), 0)
|
||||
self.assertEqual(get_apns_badge_count_future(mentioned_user), 0)
|
||||
|
||||
@@ -172,18 +172,22 @@ class UnreadCountTests(ZulipTestCase):
|
||||
@override
|
||||
def setUp(self) -> None:
|
||||
super().setUp()
|
||||
with mock.patch(
|
||||
"zerver.lib.push_notifications.push_notifications_configured", return_value=True
|
||||
) as mock_push_notifications_configured:
|
||||
hamlet = self.example_user("hamlet")
|
||||
self.register_push_device(hamlet.id)
|
||||
with (
|
||||
mock.patch(
|
||||
"zerver.lib.push_notifications.send_push_notifications"
|
||||
) as mock_send_push_notifications,
|
||||
mock.patch(
|
||||
"zerver.lib.push_notifications.push_notifications_configured", return_value=True
|
||||
) as mock_push_notifications_configured,
|
||||
):
|
||||
self.unread_msg_ids = [
|
||||
self.send_personal_message(
|
||||
self.example_user("iago"), self.example_user("hamlet"), "hello"
|
||||
),
|
||||
self.send_personal_message(
|
||||
self.example_user("iago"), self.example_user("hamlet"), "hello2"
|
||||
),
|
||||
self.send_personal_message(self.example_user("iago"), hamlet, "hello"),
|
||||
self.send_personal_message(self.example_user("iago"), hamlet, "hello2"),
|
||||
]
|
||||
mock_push_notifications_configured.assert_called()
|
||||
mock_send_push_notifications.assert_called()
|
||||
|
||||
# Sending a new message results in unread UserMessages being created
|
||||
# for users other than sender.
|
||||
@@ -858,11 +862,13 @@ class PushNotificationMarkReadFlowsTest(ZulipTestCase):
|
||||
.values_list("message_id", flat=True)
|
||||
)
|
||||
|
||||
@mock.patch("zerver.lib.push_notifications.send_push_notifications")
|
||||
@mock.patch("zerver.lib.push_notifications.push_notifications_configured", return_value=True)
|
||||
def test_track_active_mobile_push_notifications(
|
||||
self, mock_push_notifications: mock.MagicMock
|
||||
self,
|
||||
mock_push_notifications: mock.MagicMock,
|
||||
mock_send_push_notifications: mock.MagicMock,
|
||||
) -> None:
|
||||
mock_push_notifications.return_value = True
|
||||
self.login("hamlet")
|
||||
user_profile = self.example_user("hamlet")
|
||||
cordelia = self.example_user("cordelia")
|
||||
@@ -870,6 +876,7 @@ class PushNotificationMarkReadFlowsTest(ZulipTestCase):
|
||||
self.subscribe(cordelia, "test_stream")
|
||||
second_stream = self.subscribe(user_profile, "second_stream")
|
||||
self.subscribe(cordelia, "second_stream")
|
||||
self.register_push_device(user_profile.id)
|
||||
|
||||
property_name = "push_notifications"
|
||||
result = self.api_post(
|
||||
@@ -942,6 +949,7 @@ class PushNotificationMarkReadFlowsTest(ZulipTestCase):
|
||||
result = self.client_post("/json/mark_all_as_read", {})
|
||||
self.assertEqual(self.get_mobile_push_notification_ids(user_profile), [])
|
||||
mock_push_notifications.assert_called()
|
||||
mock_send_push_notifications.assert_called()
|
||||
|
||||
|
||||
class MarkAllAsReadEndpointTest(ZulipTestCase):
|
||||
|
||||
@@ -366,6 +366,7 @@ class TestNotificationData(ZulipTestCase):
|
||||
followed_topic_push_user_ids=set(),
|
||||
topic_wildcard_mention_in_followed_topic_user_ids=set(),
|
||||
stream_wildcard_mention_in_followed_topic_user_ids=set(),
|
||||
push_device_registered_user_ids=set(),
|
||||
)
|
||||
self.assertEqual(user_data.is_notifiable(acting_user_id=1000, idle=True), notifiable)
|
||||
|
||||
@@ -474,3 +475,39 @@ class TestNotificationData(ZulipTestCase):
|
||||
cordelia.id: hamlet_and_cordelia.id,
|
||||
},
|
||||
)
|
||||
|
||||
def test_push_notifiability_when_no_push_device_registered(self) -> None:
|
||||
user_data = UserMessageNotificationsData.from_user_id_sets(
|
||||
user_id=9,
|
||||
flags=[
|
||||
"mentioned",
|
||||
"topic_wildcard_mentioned",
|
||||
"stream_wildcard_mentioned",
|
||||
"topic_wildcard_mentioned",
|
||||
"stream_wildcard_mentioned",
|
||||
],
|
||||
private_message=False,
|
||||
disable_external_notifications=False,
|
||||
online_push_user_ids={9},
|
||||
dm_mention_email_disabled_user_ids=set(),
|
||||
dm_mention_push_disabled_user_ids=set(),
|
||||
all_bot_user_ids=set(),
|
||||
muted_sender_user_ids=set(),
|
||||
stream_email_user_ids=set(),
|
||||
stream_push_user_ids={9},
|
||||
topic_wildcard_mention_user_ids={9},
|
||||
stream_wildcard_mention_user_ids={9},
|
||||
followed_topic_email_user_ids=set(),
|
||||
followed_topic_push_user_ids={9},
|
||||
topic_wildcard_mention_in_followed_topic_user_ids={9},
|
||||
stream_wildcard_mention_in_followed_topic_user_ids={9},
|
||||
push_device_registered_user_ids=set(),
|
||||
)
|
||||
|
||||
self.assertFalse(user_data.online_push_enabled)
|
||||
self.assertFalse(user_data.stream_push_notify)
|
||||
self.assertFalse(user_data.topic_wildcard_mention_push_notify)
|
||||
self.assertFalse(user_data.stream_wildcard_mention_push_notify)
|
||||
self.assertFalse(user_data.followed_topic_push_notify)
|
||||
self.assertFalse(user_data.topic_wildcard_mention_in_followed_topic_push_notify)
|
||||
self.assertFalse(user_data.stream_wildcard_mention_in_followed_topic_push_notify)
|
||||
|
||||
@@ -1408,9 +1408,13 @@ class TestAPNs(PushNotificationTestCase):
|
||||
logger.output,
|
||||
)
|
||||
|
||||
@mock.patch("zerver.lib.push_notifications.send_push_notifications_legacy")
|
||||
@mock.patch("zerver.lib.push_notifications.push_notifications_configured", return_value=True)
|
||||
def test_apns_badge_count(self, mock_push_notifications: mock.MagicMock) -> None:
|
||||
def test_apns_badge_count(
|
||||
self, mock_push_notifications: mock.MagicMock, mock_send_push_notifications: mock.MagicMock
|
||||
) -> None:
|
||||
user_profile = self.example_user("othello")
|
||||
self.register_push_device_token(user_profile.id)
|
||||
# Test APNs badge count for personal messages.
|
||||
message_ids = [
|
||||
self.send_personal_message(self.sender, user_profile, "Content of message")
|
||||
@@ -1439,6 +1443,7 @@ class TestAPNs(PushNotificationTestCase):
|
||||
self.assertEqual(get_apns_badge_count_future(user_profile), num_messages - i - 1)
|
||||
|
||||
mock_push_notifications.assert_called()
|
||||
mock_send_push_notifications.assert_called()
|
||||
|
||||
|
||||
class TestGetAPNsPayload(PushNotificationTestCase):
|
||||
@@ -1516,11 +1521,15 @@ class TestGetAPNsPayload(PushNotificationTestCase):
|
||||
}
|
||||
self.assertDictEqual(payload, expected)
|
||||
|
||||
@mock.patch("zerver.lib.push_notifications.send_push_notifications_legacy")
|
||||
@mock.patch("zerver.lib.push_notifications.push_notifications_configured", return_value=True)
|
||||
def test_get_message_payload_apns_group_direct_message(
|
||||
self, mock_push_notifications: mock.MagicMock
|
||||
self,
|
||||
mock_push_notifications: mock.MagicMock,
|
||||
mock_send_push_notifications: mock.MagicMock,
|
||||
) -> None:
|
||||
user_profile = self.example_user("othello")
|
||||
self.register_push_device_token(user_profile.id)
|
||||
message_id = self.send_group_direct_message(
|
||||
self.sender, [self.example_user("othello"), self.example_user("cordelia")]
|
||||
)
|
||||
@@ -1558,6 +1567,7 @@ class TestGetAPNsPayload(PushNotificationTestCase):
|
||||
}
|
||||
self.assertDictEqual(payload, expected)
|
||||
mock_push_notifications.assert_called()
|
||||
mock_send_push_notifications.assert_called()
|
||||
|
||||
def _test_get_message_payload_apns_stream_message(
|
||||
self, trigger: str, empty_string_topic: bool = False
|
||||
|
||||
@@ -2348,6 +2348,7 @@ class RecipientInfoTest(ZulipTestCase):
|
||||
all_bot_user_ids=set(),
|
||||
topic_participant_user_ids=set(),
|
||||
sender_muted_stream=False,
|
||||
push_device_registered_user_ids=set(),
|
||||
)
|
||||
|
||||
self.assertEqual(info, expected_info)
|
||||
|
||||
@@ -1122,6 +1122,7 @@ def process_message_event(
|
||||
)
|
||||
muted_sender_user_ids = set(event_template.get("muted_sender_user_ids", []))
|
||||
all_bot_user_ids = set(event_template.get("all_bot_user_ids", []))
|
||||
push_device_registered_user_ids = set(event_template.get("push_device_registered_user_ids", []))
|
||||
disable_external_notifications = event_template.get("disable_external_notifications", False)
|
||||
user_ids_without_access_to_sender = set(
|
||||
event_template.get("user_ids_without_access_to_sender", [])
|
||||
@@ -1192,6 +1193,7 @@ def process_message_event(
|
||||
stream_wildcard_mention_in_followed_topic_user_ids=stream_wildcard_mention_in_followed_topic_user_ids,
|
||||
muted_sender_user_ids=muted_sender_user_ids,
|
||||
all_bot_user_ids=all_bot_user_ids,
|
||||
push_device_registered_user_ids=push_device_registered_user_ids,
|
||||
)
|
||||
|
||||
# Calling asdict would be slow, as it does a deep copy; pull
|
||||
@@ -1401,6 +1403,7 @@ def process_message_update_event(
|
||||
)
|
||||
muted_sender_user_ids = set(event_template.pop("muted_sender_user_ids", []))
|
||||
all_bot_user_ids = set(event_template.pop("all_bot_user_ids", []))
|
||||
push_device_registered_user_ids = set(event_template.pop("push_device_registered_user_ids", []))
|
||||
disable_external_notifications = event_template.pop("disable_external_notifications", False)
|
||||
online_push_user_ids = set(event_template.pop("online_push_user_ids", []))
|
||||
stream_name = event_template.get("stream_name")
|
||||
@@ -1445,6 +1448,7 @@ def process_message_update_event(
|
||||
stream_wildcard_mention_in_followed_topic_user_ids=stream_wildcard_mention_in_followed_topic_user_ids,
|
||||
muted_sender_user_ids=muted_sender_user_ids,
|
||||
all_bot_user_ids=all_bot_user_ids,
|
||||
push_device_registered_user_ids=push_device_registered_user_ids,
|
||||
)
|
||||
|
||||
maybe_enqueue_notifications_for_message_update(
|
||||
|
||||
Reference in New Issue
Block a user