Files
zulip/zerver/tests/test_notification_data.py
Prakhar Pratyush 1a400b21e7 notifications: Fix missed message email notifications of welcome bot.
A missed message email notification, where the message is the welcome
message sent by the welcome bot on account creation, get sent when
the user somehow not focuses the browser tab during account creation.

No missed message email or push notifications should be sent for the
messages generated by the welcome bot.

'internal_send_private_message' accepts a parameter
'disable_external_notifications' and is set to 'True' when the sender
is 'welcome bot'.

A check is introduced in `trivially_should_not_notify`, not to notify
if `disable_external_notifications` is true.

TestCases are updated to include the `disable_external_notifications`
check in the early (False) return patterns of `is_push_notifiable` and
`is_email_notifiable`.

One query reduced for both `test_create_user_with_multiple_streams`
and `test_register`.
Reason: When welcome bot sends message after user creation
`do_send_messages` calls `get_active_presence_idle_user_ids`,
`user_ids` in `get_active_presence_idle_user_ids` remains empty if
`disable_external_notifications` is true because `is_notifiable` returns
false.
`get_active_presence_idle_user_ids` calls `filter_presence_idle_user_ids`
and since the `user_ids` is empty, the query inside the function doesn't
get executed.

MissedMessageHookTest updated.

Fixes: #22884
2023-01-24 11:16:21 -08:00

354 lines
14 KiB
Python

from zerver.lib.mention import MentionBackend, MentionData
from zerver.lib.notification_data import UserMessageNotificationsData, get_user_group_mentions_data
from zerver.lib.test_classes import ZulipTestCase
from zerver.lib.user_groups import create_user_group
class TestNotificationData(ZulipTestCase):
"""
Because the `UserMessageNotificationsData` does not do any database queries, all user IDs
used in the following tests are arbitrary, and do not represent real users.
"""
def test_is_push_notifiable(self) -> None:
user_id = self.example_user("hamlet").id
acting_user_id = self.example_user("cordelia").id
# Boring case
user_data = self.create_user_notifications_data_object(user_id=user_id)
self.assertEqual(
user_data.get_push_notification_trigger(acting_user_id=acting_user_id, idle=True),
None,
)
self.assertFalse(user_data.is_push_notifiable(acting_user_id=acting_user_id, idle=True))
# Private message
user_data = self.create_user_notifications_data_object(user_id=user_id, pm_push_notify=True)
self.assertEqual(
user_data.get_push_notification_trigger(acting_user_id=acting_user_id, idle=True),
"private_message",
)
self.assertTrue(user_data.is_push_notifiable(acting_user_id=acting_user_id, idle=True))
# Mention
user_data = self.create_user_notifications_data_object(
user_id=user_id, mention_push_notify=True
)
self.assertEqual(
user_data.get_push_notification_trigger(acting_user_id=acting_user_id, idle=True),
"mentioned",
)
self.assertTrue(user_data.is_push_notifiable(acting_user_id=acting_user_id, idle=True))
# Wildcard mention
user_data = self.create_user_notifications_data_object(
user_id=user_id, wildcard_mention_push_notify=True
)
self.assertEqual(
user_data.get_push_notification_trigger(acting_user_id=acting_user_id, idle=True),
"wildcard_mentioned",
)
self.assertTrue(user_data.is_push_notifiable(acting_user_id=acting_user_id, idle=True))
# Stream notification
user_data = self.create_user_notifications_data_object(
user_id=user_id, stream_push_notify=True
)
self.assertEqual(
user_data.get_push_notification_trigger(acting_user_id=acting_user_id, idle=True),
"stream_push_notify",
)
self.assertTrue(user_data.is_push_notifiable(acting_user_id=acting_user_id, idle=True))
# Now, test the `online_push_enabled` property
# Test no notifications when not idle
user_data = self.create_user_notifications_data_object(user_id=user_id, pm_push_notify=True)
self.assertEqual(
user_data.get_push_notification_trigger(acting_user_id=acting_user_id, idle=False),
None,
)
self.assertFalse(user_data.is_push_notifiable(acting_user_id=acting_user_id, idle=False))
# Test notifications are sent when not idle but `online_push_enabled = True`
user_data = self.create_user_notifications_data_object(
user_id=user_id, online_push_enabled=True, pm_push_notify=True
)
self.assertEqual(
user_data.get_push_notification_trigger(acting_user_id=acting_user_id, idle=False),
"private_message",
)
self.assertTrue(user_data.is_push_notifiable(acting_user_id=acting_user_id, idle=False))
# Test the early (False) return patterns in these special cases:
# Message sender is muted.
user_data = self.create_user_notifications_data_object(
user_id=user_id,
sender_is_muted=True,
pm_push_notify=True,
pm_email_notify=True,
mention_push_notify=True,
mention_email_notify=True,
wildcard_mention_push_notify=True,
wildcard_mention_email_notify=True,
)
self.assertEqual(
user_data.get_push_notification_trigger(acting_user_id=acting_user_id, idle=True),
None,
)
self.assertFalse(user_data.is_push_notifiable(acting_user_id=acting_user_id, idle=True))
# Message sender is the user the object corresponds to.
user_data = self.create_user_notifications_data_object(
user_id=acting_user_id,
pm_push_notify=True,
pm_email_notify=True,
mention_push_notify=True,
mention_email_notify=True,
wildcard_mention_push_notify=True,
wildcard_mention_email_notify=True,
)
self.assertEqual(
user_data.get_push_notification_trigger(acting_user_id=acting_user_id, idle=True),
None,
)
self.assertFalse(user_data.is_push_notifiable(acting_user_id=acting_user_id, idle=True))
# 'disable_external_notifications' takes precedence over other flags.
user_data = self.create_user_notifications_data_object(
user_id=user_id,
pm_push_notify=True,
pm_email_notify=True,
mention_push_notify=True,
mention_email_notify=True,
wildcard_mention_push_notify=True,
wildcard_mention_email_notify=True,
disable_external_notifications=True,
)
self.assertEqual(
user_data.get_push_notification_trigger(acting_user_id=acting_user_id, idle=True),
None,
)
self.assertFalse(user_data.is_push_notifiable(acting_user_id=acting_user_id, idle=True))
def test_is_email_notifiable(self) -> None:
user_id = self.example_user("hamlet").id
acting_user_id = self.example_user("cordelia").id
# Boring case
user_data = self.create_user_notifications_data_object(user_id=user_id)
self.assertEqual(
user_data.get_email_notification_trigger(acting_user_id=acting_user_id, idle=True),
None,
)
self.assertFalse(user_data.is_email_notifiable(acting_user_id=acting_user_id, idle=True))
# Private message
user_data = self.create_user_notifications_data_object(
user_id=user_id, pm_email_notify=True
)
self.assertEqual(
user_data.get_email_notification_trigger(acting_user_id=acting_user_id, idle=True),
"private_message",
)
self.assertTrue(user_data.is_email_notifiable(acting_user_id=acting_user_id, idle=True))
# Mention
user_data = self.create_user_notifications_data_object(
user_id=user_id, mention_email_notify=True
)
self.assertEqual(
user_data.get_email_notification_trigger(acting_user_id=acting_user_id, idle=True),
"mentioned",
)
self.assertTrue(user_data.is_email_notifiable(acting_user_id=acting_user_id, idle=True))
# Wildcard mention
user_data = self.create_user_notifications_data_object(
user_id=user_id, wildcard_mention_email_notify=True
)
self.assertEqual(
user_data.get_email_notification_trigger(acting_user_id=acting_user_id, idle=True),
"wildcard_mentioned",
)
self.assertTrue(user_data.is_email_notifiable(acting_user_id=acting_user_id, idle=True))
# Stream notification
user_data = self.create_user_notifications_data_object(
user_id=user_id, stream_email_notify=True
)
self.assertEqual(
user_data.get_email_notification_trigger(acting_user_id=acting_user_id, idle=True),
"stream_email_notify",
)
self.assertTrue(user_data.is_email_notifiable(acting_user_id=acting_user_id, idle=True))
# Test no notifications when not idle
user_data = self.create_user_notifications_data_object(
user_id=user_id, pm_email_notify=True
)
self.assertEqual(
user_data.get_email_notification_trigger(acting_user_id=acting_user_id, idle=False),
None,
)
self.assertFalse(user_data.is_email_notifiable(acting_user_id=acting_user_id, idle=False))
# Test the early (False) return patterns in these special cases:
# Message sender is muted.
user_data = self.create_user_notifications_data_object(
user_id=user_id,
sender_is_muted=True,
pm_push_notify=True,
pm_email_notify=True,
mention_push_notify=True,
mention_email_notify=True,
wildcard_mention_push_notify=True,
wildcard_mention_email_notify=True,
)
self.assertEqual(
user_data.get_email_notification_trigger(acting_user_id=acting_user_id, idle=True),
None,
)
self.assertFalse(user_data.is_email_notifiable(acting_user_id=acting_user_id, idle=True))
# Message sender is the user the object corresponds to.
user_data = self.create_user_notifications_data_object(
user_id=acting_user_id,
pm_push_notify=True,
pm_email_notify=True,
mention_push_notify=True,
mention_email_notify=True,
wildcard_mention_push_notify=True,
wildcard_mention_email_notify=True,
)
self.assertEqual(
user_data.get_email_notification_trigger(acting_user_id=acting_user_id, idle=True),
None,
)
self.assertFalse(user_data.is_email_notifiable(acting_user_id=acting_user_id, idle=True))
# Message sender is the welcome bot.
user_data = self.create_user_notifications_data_object(
user_id=user_id,
pm_push_notify=True,
pm_email_notify=True,
mention_push_notify=True,
mention_email_notify=True,
wildcard_mention_push_notify=True,
wildcard_mention_email_notify=True,
disable_external_notifications=True,
)
self.assertEqual(
user_data.get_email_notification_trigger(acting_user_id=acting_user_id, idle=True),
None,
)
self.assertFalse(user_data.is_email_notifiable(acting_user_id=acting_user_id, idle=True))
def test_is_notifiable(self) -> None:
# This is just for coverage purposes. We've already tested all scenarios above,
# and `is_notifiable` is a simple OR of the email and push functions.
user_id = self.example_user("hamlet").id
acting_user_id = self.example_user("cordelia").id
user_data = self.create_user_notifications_data_object(user_id=user_id, pm_push_notify=True)
self.assertTrue(user_data.is_notifiable(acting_user_id=acting_user_id, idle=True))
def test_bot_user_notifiability(self) -> None:
# Non-bot user (`user_id=9`) should get notified, while bot user (`user_id=10`) shouldn't
for user_id, notifiable in [(9, True), (10, False)]:
user_data = UserMessageNotificationsData.from_user_id_sets(
user_id=user_id,
flags=["mentioned"],
private_message=True,
disable_external_notifications=False,
online_push_user_ids=set(),
pm_mention_email_disabled_user_ids=set(),
pm_mention_push_disabled_user_ids=set(),
all_bot_user_ids={10, 11, 12},
muted_sender_user_ids=set(),
stream_email_user_ids=set(),
stream_push_user_ids=set(),
wildcard_mention_user_ids=set(),
)
self.assertEqual(user_data.is_notifiable(acting_user_id=1000, idle=True), notifiable)
def test_user_group_mentions_map(self) -> None:
hamlet = self.example_user("hamlet")
cordelia = self.example_user("cordelia")
realm = hamlet.realm
hamlet_only = create_user_group("hamlet_only", [hamlet], realm, acting_user=None)
hamlet_and_cordelia = create_user_group(
"hamlet_and_cordelia", [hamlet, cordelia], realm, acting_user=None
)
mention_backend = MentionBackend(realm.id)
# Base case. No user/user group mentions
result = get_user_group_mentions_data(
mentioned_user_ids=set(),
mentioned_user_group_ids=[],
mention_data=MentionData(mention_backend, "no group mentioned"),
)
self.assertDictEqual(result, {})
# Only user group mentions, no personal mentions
result = get_user_group_mentions_data(
mentioned_user_ids=set(),
mentioned_user_group_ids=[hamlet_and_cordelia.id],
mention_data=MentionData(mention_backend, "hey @*hamlet_and_cordelia*!"),
)
self.assertDictEqual(
result,
{
hamlet.id: hamlet_and_cordelia.id,
cordelia.id: hamlet_and_cordelia.id,
},
)
# Hamlet is mentioned in two user groups
# Test that we consider the smaller user group
result = get_user_group_mentions_data(
mentioned_user_ids=set(),
mentioned_user_group_ids=[hamlet_and_cordelia.id, hamlet_only.id],
mention_data=MentionData(
mention_backend, "hey @*hamlet_and_cordelia* and @*hamlet_only*"
),
)
self.assertDictEqual(
result,
{
hamlet.id: hamlet_only.id,
cordelia.id: hamlet_and_cordelia.id,
},
)
# To make sure we aren't getting the expected data from over-writing in a loop,
# test the same setup as above, but with reversed group ids.
result = get_user_group_mentions_data(
mentioned_user_ids=set(),
mentioned_user_group_ids=[hamlet_only.id, hamlet_and_cordelia.id],
mention_data=MentionData(
mention_backend, "hey @*hamlet_only* and @*hamlet_and_cordelia*"
),
)
self.assertDictEqual(
result,
{
hamlet.id: hamlet_only.id,
cordelia.id: hamlet_and_cordelia.id,
},
)
# Personal and user group mentioned. Test that we don't consider the user
# group mention for Hamlet in this case.
result = get_user_group_mentions_data(
mentioned_user_ids={hamlet.id},
mentioned_user_group_ids=[hamlet_and_cordelia.id],
mention_data=MentionData(mention_backend, "hey @*hamlet_and_cordelia*!"),
)
self.assertDictEqual(
result,
{
cordelia.id: hamlet_and_cordelia.id,
},
)