diff --git a/zerver/lib/digest.py b/zerver/lib/digest.py index 6c5e7c2edc..60211de8c6 100644 --- a/zerver/lib/digest.py +++ b/zerver/lib/digest.py @@ -348,7 +348,11 @@ def get_digest_context(user: UserProfile, cutoff: float) -> Dict[str, Any]: def bulk_handle_digest_email(user_ids: List[int], cutoff: float) -> None: # We go directly to the database to get user objects, # since inactive users are likely to not be in the cache. - users = UserProfile.objects.filter(id__in=user_ids).order_by("id").select_related("realm") + users = ( + UserProfile.objects.filter(id__in=user_ids, is_active=True, realm__deactivated=False) + .order_by("id") + .select_related("realm") + ) context_map = bulk_get_digest_context(users, cutoff) digest_users = [] diff --git a/zerver/tests/test_digest.py b/zerver/tests/test_digest.py index 0fef4d3d13..87c255bdb6 100644 --- a/zerver/tests/test_digest.py +++ b/zerver/tests/test_digest.py @@ -8,6 +8,7 @@ from django.utils.timezone import now as timezone_now from confirmation.models import one_click_unsubscribe_link from zerver.actions.create_user import do_create_user +from zerver.actions.users import do_deactivate_user from zerver.lib.digest import ( DigestTopic, _enqueue_emails_for_realm, @@ -86,6 +87,32 @@ class TestDigestEmailMessages(ZulipTestCase): self.assertIn("some content", teaser_messages[0]["content"][0]["plain"]) self.assertIn(teaser_messages[0]["sender"], expected_participants) + def test_bulk_handle_digest_email_skips_deactivated_users(self) -> None: + """ + A user id may be added to the queue before the user is deactivated. In such a case, + the function responsible for sending the email should correctly skip them. + """ + realm = get_realm("zulip") + hamlet = self.example_user("hamlet") + user_ids = list( + UserProfile.objects.filter(is_bot=False, realm=realm).values_list("id", flat=True) + ) + + do_deactivate_user(hamlet, acting_user=None) + + with mock.patch("zerver.lib.digest.enough_traffic", return_value=True), mock.patch( + "zerver.lib.digest.send_future_email" + ) as mock_send_email: + bulk_handle_digest_email(user_ids, 1) + + emailed_user_ids = [ + call_args[1]["to_user_ids"][0] for call_args in mock_send_email.call_args_list + ] + + self.assertEqual( + set(emailed_user_ids), set(user_id for user_id in user_ids if user_id != hamlet.id) + ) + @mock.patch("zerver.lib.digest.enough_traffic") @mock.patch("zerver.lib.digest.send_future_email") def test_guest_user_multiple_stream_sender(