def consume(self, event: Mapping[str, Any]) -> None: if "user_ids" in event: user_ids = event["user_ids"] else: # legacy code may have enqueued a single id user_ids = [event["user_profile_id"]] bulk_handle_digest_email(user_ids, event["cutoff"])
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), {user_id for user_id in user_ids if user_id != hamlet.id})
def test_multiple_stream_senders( self, mock_send_future_email: mock.MagicMock, mock_enough_traffic: mock.MagicMock) -> None: othello = self.example_user('othello') self.subscribe(othello, 'Verona') one_day_ago = timezone_now() - datetime.timedelta(days=1) Message.objects.all().update(date_sent=one_day_ago) one_hour_ago = timezone_now() - datetime.timedelta(seconds=3600) cutoff = time.mktime(one_hour_ago.timetuple()) senders = ['hamlet', 'cordelia', 'iago', 'prospero', 'ZOE'] self.simulate_stream_conversation('Verona', senders) # Remove RealmAuditoLog rows, so we don't exclude polonius. RealmAuditLog.objects.all().delete() flush_per_request_caches() # When this test is run in isolation, one additional query is run which # is equivalent to # ContentType.objects.get(app_label='zerver', model='userprofile') # This code is run when we call `confirmation.models.create_confirmation_link`. # To trigger this, we call the one_click_unsubscribe_link function below. one_click_unsubscribe_link(othello, 'digest') with queries_captured() as queries: bulk_handle_digest_email([othello.id], cutoff) self.assert_length(queries, 9) self.assertEqual(mock_send_future_email.call_count, 1) kwargs = mock_send_future_email.call_args[1] self.assertEqual(kwargs['to_user_ids'], [othello.id]) hot_convo = kwargs['context']['hot_conversations'][0] expected_participants = { self.example_user(sender).full_name for sender in senders } self.assertEqual(set(hot_convo['participants']), expected_participants) self.assertEqual(hot_convo['count'], 5 - 2) # 5 messages, but 2 shown teaser_messages = hot_convo['first_few_messages'][0]['senders'] self.assertIn('some content', teaser_messages[0]['content'][0]['plain']) self.assertIn(teaser_messages[0]['sender'], expected_participants)
def test_guest_user_multiple_stream_sender( self, mock_send_future_email: mock.MagicMock, mock_enough_traffic: mock.MagicMock) -> None: othello = self.example_user('othello') hamlet = self.example_user('hamlet') cordelia = self.example_user('cordelia') polonius = self.example_user('polonius') create_stream_if_needed(cordelia.realm, 'web_public_stream', is_web_public=True) self.subscribe(othello, 'web_public_stream') self.subscribe(hamlet, 'web_public_stream') self.subscribe(cordelia, 'web_public_stream') self.subscribe(polonius, 'web_public_stream') one_day_ago = timezone_now() - datetime.timedelta(days=1) Message.objects.all().update(date_sent=one_day_ago) one_hour_ago = timezone_now() - datetime.timedelta(seconds=3600) cutoff = time.mktime(one_hour_ago.timetuple()) senders = ['hamlet', 'cordelia', 'othello', 'desdemona'] self.simulate_stream_conversation('web_public_stream', senders) # Remove RealmAuditoLog rows, so we don't exclude polonius. RealmAuditLog.objects.all().delete() flush_per_request_caches() # When this test is run in isolation, one additional query is run which # is equivalent to # ContentType.objects.get(app_label='zerver', model='userprofile') # This code is run when we call `confirmation.models.create_confirmation_link`. # To trigger this, we call the one_click_unsubscribe_link function below. one_click_unsubscribe_link(polonius, 'digest') with queries_captured() as queries: bulk_handle_digest_email([polonius.id], cutoff) self.assert_length(queries, 9) self.assertEqual(mock_send_future_email.call_count, 1) kwargs = mock_send_future_email.call_args[1] self.assertEqual(kwargs['to_user_ids'], [polonius.id]) new_stream_names = kwargs['context']['new_streams']['plain'] self.assertTrue('web_public_stream' in new_stream_names)
def test_soft_deactivated_user_multiple_stream_senders(self) -> None: one_day_ago = timezone_now() - datetime.timedelta(days=1) Message.objects.all().update(date_sent=one_day_ago) digest_users = [ self.example_user('othello'), self.example_user('aaron'), self.example_user('desdemona'), self.example_user('polonius'), ] digest_users.sort(key=lambda user: user.id) for digest_user in digest_users: for stream in ['Verona', 'Scotland', 'Denmark']: self.subscribe(digest_user, stream) RealmAuditLog.objects.all().delete() # Send messages to a stream and unsubscribe - subscribe from that stream senders = ['hamlet', 'cordelia', 'iago', 'prospero', 'ZOE'] self.simulate_stream_conversation('Verona', senders) for digest_user in digest_users: self.unsubscribe(digest_user, 'Verona') self.subscribe(digest_user, 'Verona') # Send messages to other streams self.simulate_stream_conversation('Scotland', senders) self.simulate_stream_conversation('Denmark', senders) one_hour_ago = timezone_now() - datetime.timedelta(seconds=3600) cutoff = time.mktime(one_hour_ago.timetuple()) flush_per_request_caches() # When this test is run in isolation, one additional query is run which # is equivalent to # ContentType.objects.get(app_label='zerver', model='userprofile') # This code is run when we call `confirmation.models.create_confirmation_link`. # To trigger this, we call the one_click_unsubscribe_link function below. one_click_unsubscribe_link(digest_users[0], 'digest') with mock.patch('zerver.lib.digest.send_future_email' ) as mock_send_future_email: digest_user_ids = [user.id for user in digest_users] with queries_captured() as queries: with cache_tries_captured() as cache_tries: bulk_handle_digest_email(digest_user_ids, cutoff) self.assert_length(queries, 12) self.assert_length(cache_tries, 0) self.assertEqual(mock_send_future_email.call_count, len(digest_users)) for i, digest_user in enumerate(digest_users): kwargs = mock_send_future_email.call_args_list[i][1] self.assertEqual(kwargs['to_user_ids'], [digest_user.id]) hot_conversations = kwargs['context']['hot_conversations'] self.assertEqual(2, len(hot_conversations), [digest_user.id]) hot_convo = hot_conversations[0] expected_participants = { self.example_user(sender).full_name for sender in senders } self.assertEqual(set(hot_convo['participants']), expected_participants) self.assertEqual(hot_convo['count'], 5 - 2) # 5 messages, but 2 shown teaser_messages = hot_convo['first_few_messages'][0]['senders'] self.assertIn('some content', teaser_messages[0]['content'][0]['plain']) self.assertIn(teaser_messages[0]['sender'], expected_participants) last_message_id = get_last_message_id() for digest_user in digest_users: log_rows = RealmAuditLog.objects.filter( modified_user_id=digest_user.id, event_type=RealmAuditLog.USER_DIGEST_EMAIL_CREATED, ) (log, ) = log_rows self.assertEqual(log.event_last_message_id, last_message_id)