Пример #1
0
def cleanup_event_queue(request: HttpRequest, user_profile: UserProfile,
                        queue_id: str=REQ()) -> HttpResponse:
    client = get_client_descriptor(str(queue_id))
    if client is None:
        raise BadEventQueueIdError(queue_id)
    if user_profile.id != client.user_profile_id:
        return json_error(_("You are not authorized to access this queue"))
    request._log_data['extra'] = "[%s]" % (queue_id,)
    client.cleanup()
    return json_success()
Пример #2
0
def cleanup_event_queue(request: HttpRequest, user_profile: UserProfile,
                        queue_id: str=REQ()) -> HttpResponse:
    client = get_client_descriptor(str(queue_id))
    if client is None:
        raise BadEventQueueIdError(queue_id)
    if user_profile.id != client.user_profile_id:
        return json_error(_("You are not authorized to access this queue"))
    request._log_data['extra'] = "[%s]" % (queue_id,)
    client.cleanup()
    return json_success()
Пример #3
0
def cleanup_event_queue(request, user_profile, queue_id=REQ()):
    # type: (HttpRequest, UserProfile, text_type) -> HttpResponse
    client = get_client_descriptor(str(queue_id))
    if client is None:
        return json_error(_("Bad event queue id: %s") % (queue_id,))
    if user_profile.id != client.user_profile_id:
        return json_error(_("You are not authorized to access this queue"))
    request._log_data['extra'] = "[%s]" % (queue_id,)
    client.cleanup()
    return json_success()
Пример #4
0
def cleanup_event_queue(request, user_profile, queue_id=REQ()):
    # type: (HttpRequest, UserProfile, text_type) -> HttpResponse
    client = get_client_descriptor(str(queue_id))
    if client is None:
        return json_error(_("Bad event queue id: %s") % (queue_id, ))
    if user_profile.id != client.user_profile_id:
        return json_error(_("You are not authorized to access this queue"))
    request._log_data['extra'] = "[%s]" % (queue_id, )
    client.cleanup()
    return json_success()
Пример #5
0
 def allocate_event_queue() -> ClientDescriptor:
     result = self.tornado_call(get_events, user_profile,
                                {"apply_markdown": ujson.dumps(True),
                                 "client_gravatar": ujson.dumps(True),
                                 "event_types": ujson.dumps(["message"]),
                                 "user_client": "website",
                                 "dont_block": ujson.dumps(True)})
     self.assert_json_success(result)
     queue_id = ujson.loads(result.content)["queue_id"]
     return get_client_descriptor(queue_id)
Пример #6
0
    def authenticate_client(self, msg: Dict[str, Any]) -> None:
        if self.authenticated:
            self.session.send_message({'req_id': msg['req_id'], 'type': 'response',
                                       'response': {'result': 'error',
                                                    'msg': 'Already authenticated'}})
            return

        user_profile = get_user_profile(self.browser_session_id)
        if user_profile is None:
            raise JsonableError(_('Unknown or missing session'))
        self.session.user_profile = user_profile

        if 'csrf_token' not in msg['request']:
            # Debugging code to help with understanding #6961
            logging.error("CSRF token missing from websockets auth request: %s" % (msg['request'],))
            raise JsonableError(_('CSRF token entry missing from request'))
        if not _compare_salted_tokens(msg['request']['csrf_token'], self.csrf_token):
            raise JsonableError(_('CSRF token does not match that in cookie'))

        if 'queue_id' not in msg['request']:
            raise JsonableError(_("Missing 'queue_id' argument"))

        queue_id = msg['request']['queue_id']
        client = get_client_descriptor(queue_id)
        if client is None:
            raise BadEventQueueIdError(queue_id)

        if user_profile.id != client.user_profile_id:
            raise JsonableError(_("You are not the owner of the queue with id '%s'") % (queue_id,))

        self.authenticated = True
        register_connection(queue_id, self)

        response = {'req_id': msg['req_id'], 'type': 'response',
                    'response': {'result': 'success', 'msg': ''}}

        status_inquiries = msg['request'].get('status_inquiries')
        if status_inquiries is not None:
            results = {}  # type: Dict[str, Dict[str, str]]
            for inquiry in status_inquiries:
                status = redis_client.hgetall(req_redis_key(inquiry))  # type: Dict[bytes, bytes]
                if len(status) == 0:
                    result = {'status': 'not_received'}
                elif b'response' not in status:
                    result = {'status': status[b'status'].decode('utf-8')}
                else:
                    result = {'status': status[b'status'].decode('utf-8'),
                              'response': ujson.loads(status[b'response'])}
                results[str(inquiry)] = result
            response['response']['status_inquiries'] = results

        self.session.send_message(response)
        ioloop = tornado.ioloop.IOLoop.instance()
        ioloop.remove_timeout(self.timeout_handle)
Пример #7
0
    def authenticate_client(self, msg: Dict[str, Any]) -> None:
        if self.authenticated:
            self.session.send_message({'req_id': msg['req_id'], 'type': 'response',
                                       'response': {'result': 'error',
                                                    'msg': 'Already authenticated'}})
            return

        user_profile = get_user_profile(self.browser_session_id)
        if user_profile is None:
            raise JsonableError(_('Unknown or missing session'))
        self.session.user_profile = user_profile

        if 'csrf_token' not in msg['request']:
            # Debugging code to help with understanding #6961
            logging.error("Invalid websockets auth request: %s" % (msg['request'],))
            raise JsonableError(_('CSRF token entry missing from request'))
        if not _compare_salted_tokens(msg['request']['csrf_token'], self.csrf_token):
            raise JsonableError(_('CSRF token does not match that in cookie'))

        if 'queue_id' not in msg['request']:
            raise JsonableError(_("Missing 'queue_id' argument"))

        queue_id = msg['request']['queue_id']
        client = get_client_descriptor(queue_id)
        if client is None:
            raise BadEventQueueIdError(queue_id)

        if user_profile.id != client.user_profile_id:
            raise JsonableError(_("You are not the owner of the queue with id '%s'") % (queue_id,))

        self.authenticated = True
        register_connection(queue_id, self)

        response = {'req_id': msg['req_id'], 'type': 'response',
                    'response': {'result': 'success', 'msg': ''}}

        status_inquiries = msg['request'].get('status_inquiries')
        if status_inquiries is not None:
            results = {}  # type: Dict[str, Dict[str, str]]
            for inquiry in status_inquiries:
                status = redis_client.hgetall(req_redis_key(inquiry))  # type: Dict[bytes, bytes]
                if len(status) == 0:
                    result = {'status': 'not_received'}
                elif b'response' not in status:
                    result = {'status': status[b'status'].decode('utf-8')}
                else:
                    result = {'status': status[b'status'].decode('utf-8'),
                              'response': ujson.loads(status[b'response'])}
                results[str(inquiry)] = result
            response['response']['status_inquiries'] = results

        self.session.send_message(response)
        ioloop = tornado.ioloop.IOLoop.instance()
        ioloop.remove_timeout(self.timeout_handle)
Пример #8
0
def cleanup_event_queue(
    request: HttpRequest, user_profile: UserProfile, queue_id: str = REQ()
) -> HttpResponse:
    client = get_client_descriptor(str(queue_id))
    if client is None:
        raise BadEventQueueIdError(queue_id)
    if user_profile.id != client.user_profile_id:
        raise JsonableError(_("You are not authorized to access this queue"))
    log_data = get_request_notes(request).log_data
    assert log_data is not None
    log_data["extra"] = f"[{queue_id}]"
    client.cleanup()
    return json_success()
Пример #9
0
    def authenticate_client(self, msg):
        # type: (Dict[str, Any]) -> None
        if self.authenticated:
            self.session.send_message({'req_id': msg['req_id'], 'type': 'response',
                                       'response': {'result': 'error', 'msg': 'Already authenticated'}})
            return

        user_profile = get_user_profile(self.browser_session_id)
        if user_profile is None:
            raise SocketAuthError('Unknown or missing session')
        self.session.user_profile = user_profile

        if not _compare_salted_tokens(msg['request']['csrf_token'], self.csrf_token):
            raise SocketAuthError('CSRF token does not match that in cookie')

        if 'queue_id' not in msg['request']:
            raise SocketAuthError("Missing 'queue_id' argument")

        queue_id = msg['request']['queue_id']
        client = get_client_descriptor(queue_id)
        if client is None:
            raise SocketAuthError('Bad event queue id: %s' % (queue_id,))

        if user_profile.id != client.user_profile_id:
            raise SocketAuthError("You are not the owner of the queue with id '%s'" % (queue_id,))

        self.authenticated = True
        register_connection(queue_id, self)

        response = {'req_id': msg['req_id'], 'type': 'response',
                    'response': {'result': 'success', 'msg': ''}}

        status_inquiries = msg['request'].get('status_inquiries')
        if status_inquiries is not None:
            results = {}
            for inquiry in status_inquiries:
                status = redis_client.hgetall(req_redis_key(inquiry))
                if len(status) == 0:
                    status['status'] = 'not_received'
                if 'response' in status:
                    status['response'] = ujson.loads(status['response'])
                results[str(inquiry)] = status
            response['response']['status_inquiries'] = results

        self.session.send_message(response)
        ioloop = tornado.ioloop.IOLoop.instance()
        ioloop.remove_timeout(self.timeout_handle)
Пример #10
0
    def authenticate_client(self, msg):
        # type: (Dict[str, Any]) -> None
        if self.authenticated:
            self.session.send_message({'req_id': msg['req_id'], 'type': 'response',
                                       'response': {'result': 'error', 'msg': 'Already authenticated'}})
            return

        user_profile = get_user_profile(self.browser_session_id)
        if user_profile is None:
            raise SocketAuthError('Unknown or missing session')
        self.session.user_profile = user_profile

        if not _compare_salted_tokens(msg['request']['csrf_token'], self.csrf_token):
            raise SocketAuthError('CSRF token does not match that in cookie')

        if 'queue_id' not in msg['request']:
            raise SocketAuthError("Missing 'queue_id' argument")

        queue_id = msg['request']['queue_id']
        client = get_client_descriptor(queue_id)
        if client is None:
            raise SocketAuthError('Bad event queue id: %s' % (queue_id,))

        if user_profile.id != client.user_profile_id:
            raise SocketAuthError("You are not the owner of the queue with id '%s'" % (queue_id,))

        self.authenticated = True
        register_connection(queue_id, self)

        response = {'req_id': msg['req_id'], 'type': 'response',
                    'response': {'result': 'success', 'msg': ''}}

        status_inquiries = msg['request'].get('status_inquiries')
        if status_inquiries is not None:
            results = {}
            for inquiry in status_inquiries:
                status = redis_client.hgetall(req_redis_key(inquiry))
                if len(status) == 0:
                    status['status'] = 'not_received'
                if 'response' in status:
                    status['response'] = ujson.loads(status['response'])
                results[str(inquiry)] = status
            response['response']['status_inquiries'] = results

        self.session.send_message(response)
        ioloop = tornado.ioloop.IOLoop.instance()
        ioloop.remove_timeout(self.timeout_handle)
Пример #11
0
    def test_end_to_end_missedmessage_hook(self) -> None:
        """Tests what arguments missedmessage_hook passes into maybe_enqueue_notifications.
        Combined with the previous test, this ensures that the missedmessage_hook is correct"""
        user_profile = self.example_user('hamlet')
        email = user_profile.email
        self.login(email)

        def change_subscription_properties(
                user_profile: UserProfile, stream: Stream, sub: Subscription,
                properties: Dict[str, bool]) -> None:
            for property_name, value in properties.items():
                do_change_subscription_property(user_profile, sub, stream,
                                                property_name, value)

        result = self.tornado_call(
            get_events, user_profile, {
                "apply_markdown": ujson.dumps(True),
                "client_gravatar": ujson.dumps(True),
                "event_types": ujson.dumps(["message"]),
                "user_client": "website",
                "dont_block": ujson.dumps(True),
            })
        self.assert_json_success(result)
        queue_id = ujson.loads(result.content)["queue_id"]
        client_descriptor = get_client_descriptor(queue_id)

        with mock.patch(
                "zerver.tornado.event_queue.maybe_enqueue_notifications"
        ) as mock_enqueue:
            # To test the missed_message hook, we first need to send a message
            msg_id = self.send_stream_message(self.example_email("iago"),
                                              "Denmark")

            # Verify that nothing happens if you call it as not the
            # "last client descriptor", in which case the function
            # short-circuits, since the `missedmessage_hook` handler
            # for garbage-collection is only for the user's last queue.
            missedmessage_hook(user_profile.id, client_descriptor, False)
            mock_enqueue.assert_not_called()

            # Now verify that we called the appropriate enqueue function
            missedmessage_hook(user_profile.id, client_descriptor, True)
            mock_enqueue.assert_called_once()
            args_list = mock_enqueue.call_args_list[0][0]

            self.assertEqual(args_list,
                             (user_profile.id, msg_id, False, False, False,
                              False, "Denmark", False, True, {
                                  'email_notified': False,
                                  'push_notified': False
                              }))

        # Clear the event queue, before repeating with a private message
        client_descriptor.event_queue.pop()
        self.assertTrue(client_descriptor.event_queue.empty())
        msg_id = self.send_personal_message(self.example_email("iago"), email)
        with mock.patch(
                "zerver.tornado.event_queue.maybe_enqueue_notifications"
        ) as mock_enqueue:
            missedmessage_hook(user_profile.id, client_descriptor, True)
            mock_enqueue.assert_called_once()
            args_list = mock_enqueue.call_args_list[0][0]

            self.assertEqual(args_list, (user_profile.id, msg_id, True, False,
                                         False, False, None, False, True, {
                                             'email_notified': True,
                                             'push_notified': True
                                         }))

        # Clear the event queue, now repeat with a mention
        client_descriptor.event_queue.pop()
        self.assertTrue(client_descriptor.event_queue.empty())
        msg_id = self.send_stream_message(
            self.example_email("iago"),
            "Denmark",
            content="@**King Hamlet** what's up?")
        with mock.patch(
                "zerver.tornado.event_queue.maybe_enqueue_notifications"
        ) as mock_enqueue:
            # Clear the event queue, before repeating with a private message
            missedmessage_hook(user_profile.id, client_descriptor, True)
            mock_enqueue.assert_called_once()
            args_list = mock_enqueue.call_args_list[0][0]

            self.assertEqual(args_list,
                             (user_profile.id, msg_id, False, True, False,
                              False, "Denmark", False, True, {
                                  'email_notified': True,
                                  'push_notified': True
                              }))

        stream = get_stream("Denmark", user_profile.realm)
        sub = Subscription.objects.get(user_profile=user_profile,
                                       recipient__type=Recipient.STREAM,
                                       recipient__type_id=stream.id)

        # Clear the event queue, now repeat with stream message with stream_push_notify
        change_subscription_properties(user_profile, stream, sub,
                                       {'push_notifications': True})
        client_descriptor.event_queue.pop()
        self.assertTrue(client_descriptor.event_queue.empty())
        msg_id = self.send_stream_message(self.example_email("iago"),
                                          "Denmark",
                                          content="what's up everyone?")
        with mock.patch(
                "zerver.tornado.event_queue.maybe_enqueue_notifications"
        ) as mock_enqueue:
            # Clear the event queue, before repeating with a private message
            missedmessage_hook(user_profile.id, client_descriptor, True)
            mock_enqueue.assert_called_once()
            args_list = mock_enqueue.call_args_list[0][0]

            self.assertEqual(args_list, (user_profile.id, msg_id, False, False,
                                         True, False, "Denmark", False, True, {
                                             'email_notified': False,
                                             'push_notified': False
                                         }))

        # Clear the event queue, now repeat with stream message with stream_email_notify
        change_subscription_properties(user_profile, stream, sub, {
            'push_notifications': False,
            'email_notifications': True
        })
        client_descriptor.event_queue.pop()
        self.assertTrue(client_descriptor.event_queue.empty())
        msg_id = self.send_stream_message(self.example_email("iago"),
                                          "Denmark",
                                          content="what's up everyone?")
        with mock.patch(
                "zerver.tornado.event_queue.maybe_enqueue_notifications"
        ) as mock_enqueue:
            # Clear the event queue, before repeating with a private message
            missedmessage_hook(user_profile.id, client_descriptor, True)
            mock_enqueue.assert_called_once()
            args_list = mock_enqueue.call_args_list[0][0]

            self.assertEqual(args_list, (user_profile.id, msg_id, False, False,
                                         False, True, "Denmark", False, True, {
                                             'email_notified': False,
                                             'push_notified': False
                                         }))

        # Clear the event queue, now repeat with stream message with stream_push_notify
        # on a muted topic, which we should not push notify for
        change_subscription_properties(user_profile, stream, sub, {
            'push_notifications': True,
            'email_notifications': False
        })
        client_descriptor.event_queue.pop()
        self.assertTrue(client_descriptor.event_queue.empty())
        do_mute_topic(user_profile, stream, sub.recipient, "mutingtest")
        msg_id = self.send_stream_message(self.example_email("iago"),
                                          "Denmark",
                                          content="what's up everyone?",
                                          topic_name="mutingtest")
        with mock.patch(
                "zerver.tornado.event_queue.maybe_enqueue_notifications"
        ) as mock_enqueue:
            # Clear the event queue, before repeating with a private message
            missedmessage_hook(user_profile.id, client_descriptor, True)
            mock_enqueue.assert_called_once()
            args_list = mock_enqueue.call_args_list[0][0]

            self.assertEqual(args_list,
                             (user_profile.id, msg_id, False, False, False,
                              False, "Denmark", False, True, {
                                  'email_notified': False,
                                  'push_notified': False
                              }))

        # Clear the event queue, now repeat with stream message with stream_email_notify
        # on a muted stream, which we should not email notify for
        change_subscription_properties(user_profile, stream, sub, {
            'push_notifications': False,
            'email_notifications': True
        })
        client_descriptor.event_queue.pop()
        self.assertTrue(client_descriptor.event_queue.empty())
        change_subscription_properties(user_profile, stream, sub,
                                       {'in_home_view': False})
        msg_id = self.send_stream_message(self.example_email("iago"),
                                          "Denmark",
                                          content="what's up everyone?")
        with mock.patch(
                "zerver.tornado.event_queue.maybe_enqueue_notifications"
        ) as mock_enqueue:
            # Clear the event queue, before repeating with a private message
            missedmessage_hook(user_profile.id, client_descriptor, True)
            mock_enqueue.assert_called_once()
            args_list = mock_enqueue.call_args_list[0][0]

            self.assertEqual(args_list,
                             (user_profile.id, msg_id, False, False, False,
                              False, "Denmark", False, True, {
                                  'email_notified': False,
                                  'push_notified': False
                              }))

        # Clean up the state we just changed (not necessary unless we add more test code below)
        change_subscription_properties(user_profile, stream, sub, {
            'push_notifications': True,
            'in_home_view': True
        })
Пример #12
0
    def test_end_to_end_missedmessage_hook(self):
        # type: () -> None
        """Tests what arguments missedmessage_hook passes into maybe_enqueue_notifications.
        Combined with the previous test, this ensures that the missedmessage_hook is correct"""
        user_profile = self.example_user('hamlet')
        email = user_profile.email
        self.login(email)

        result = self.tornado_call(get_events_backend, user_profile,
                                   {"apply_markdown": ujson.dumps(True),
                                    "event_types": ujson.dumps(["message"]),
                                    "user_client": "website",
                                    "dont_block": ujson.dumps(True),
                                    })
        self.assert_json_success(result)
        queue_id = ujson.loads(result.content)["queue_id"]
        client_descriptor = get_client_descriptor(queue_id)

        with mock.patch("zerver.tornado.event_queue.maybe_enqueue_notifications") as mock_enqueue:
            # To test the missed_message hook, we first need to send a message
            msg_id = self.send_stream_message(self.example_email("iago"), "Denmark")

            # Verify that nothing happens if you call it as not the
            # "last client descriptor", in which case the function
            # short-circuits, since the `missedmessage_hook` handler
            # for garbage-collection is only for the user's last queue.
            missedmessage_hook(user_profile.id, client_descriptor, False)
            mock_enqueue.assert_not_called()

            # Now verify that we called the appropriate enqueue function
            missedmessage_hook(user_profile.id, client_descriptor, True)
            mock_enqueue.assert_called_once()
            args_list = mock_enqueue.call_args_list[0][0]

            self.assertEqual(args_list, (user_profile.id, msg_id, False, False, False,
                                         "Denmark", False, True,
                                         {'email_notified': False, 'push_notified': False}))

        # Clear the event queue, before repeating with a private message
        client_descriptor.event_queue.pop()
        self.assertTrue(client_descriptor.event_queue.empty())
        msg_id = self.send_personal_message(self.example_email("iago"), email)
        with mock.patch("zerver.tornado.event_queue.maybe_enqueue_notifications") as mock_enqueue:
            missedmessage_hook(user_profile.id, client_descriptor, True)
            mock_enqueue.assert_called_once()
            args_list = mock_enqueue.call_args_list[0][0]

            self.assertEqual(args_list, (user_profile.id, msg_id, True, False,
                                         False, None, False, True,
                                         {'email_notified': True, 'push_notified': True}))

        # Clear the event queue, now repeat with a mention
        client_descriptor.event_queue.pop()
        self.assertTrue(client_descriptor.event_queue.empty())
        msg_id = self.send_stream_message(self.example_email("iago"), "Denmark",
                                          content="@**King Hamlet** what's up?")
        with mock.patch("zerver.tornado.event_queue.maybe_enqueue_notifications") as mock_enqueue:
            # Clear the event queue, before repeating with a private message
            missedmessage_hook(user_profile.id, client_descriptor, True)
            mock_enqueue.assert_called_once()
            args_list = mock_enqueue.call_args_list[0][0]

            self.assertEqual(args_list, (user_profile.id, msg_id, False, True,
                                         False, "Denmark", False, True,
                                         {'email_notified': True, 'push_notified': True}))

        # Clear the event queue, now repeat with stream message with stream_push_notify
        stream = get_stream("Denmark", user_profile.realm)
        sub = Subscription.objects.get(user_profile=user_profile, recipient__type=Recipient.STREAM,
                                       recipient__type_id=stream.id)
        sub.push_notifications = True
        sub.save()
        client_descriptor.event_queue.pop()
        self.assertTrue(client_descriptor.event_queue.empty())
        msg_id = self.send_stream_message(self.example_email("iago"), "Denmark",
                                          content="what's up everyone?")
        with mock.patch("zerver.tornado.event_queue.maybe_enqueue_notifications") as mock_enqueue:
            # Clear the event queue, before repeating with a private message
            missedmessage_hook(user_profile.id, client_descriptor, True)
            mock_enqueue.assert_called_once()
            args_list = mock_enqueue.call_args_list[0][0]

            self.assertEqual(args_list, (user_profile.id, msg_id, False, False,
                                         True, "Denmark", False, True,
                                         {'email_notified': False, 'push_notified': False}))

        # Clear the event queue, now repeat with stream message with stream_push_notify
        # on a muted topic, which we should not push notify for
        client_descriptor.event_queue.pop()
        self.assertTrue(client_descriptor.event_queue.empty())
        do_mute_topic(user_profile, stream, sub.recipient, "mutingtest")
        msg_id = self.send_stream_message(self.example_email("iago"), "Denmark",
                                          content="what's up everyone?", topic_name="mutingtest")
        with mock.patch("zerver.tornado.event_queue.maybe_enqueue_notifications") as mock_enqueue:
            # Clear the event queue, before repeating with a private message
            missedmessage_hook(user_profile.id, client_descriptor, True)
            mock_enqueue.assert_called_once()
            args_list = mock_enqueue.call_args_list[0][0]

            self.assertEqual(args_list, (user_profile.id, msg_id, False, False,
                                         False, "Denmark", False, True,
                                         {'email_notified': False, 'push_notified': False}))

        # Clear the event queue, now repeat with stream message with stream_push_notify
        # on a muted stream, which we should not push notify for
        client_descriptor.event_queue.pop()
        self.assertTrue(client_descriptor.event_queue.empty())
        sub.in_home_view = False
        sub.save()
        msg_id = self.send_stream_message(self.example_email("iago"), "Denmark",
                                          content="what's up everyone?")
        with mock.patch("zerver.tornado.event_queue.maybe_enqueue_notifications") as mock_enqueue:
            # Clear the event queue, before repeating with a private message
            missedmessage_hook(user_profile.id, client_descriptor, True)
            mock_enqueue.assert_called_once()
            args_list = mock_enqueue.call_args_list[0][0]

            self.assertEqual(args_list, (user_profile.id, msg_id, False, False,
                                         False, "Denmark", False, True,
                                         {'email_notified': False, 'push_notified': False}))

        # Clean up the state we just changed (not necessary unless we add more test code below)
        sub.push_notifications = True
        sub.in_home_view = True
        sub.save()
Пример #13
0
    def test_end_to_end_missedmessage_hook(self):
        # type: () -> None
        """Tests what arguments missedmessage_hook passes into maybe_enqueue_notifications.
        Combined with the previous test, this ensures that the missedmessage_hook is correct"""
        user_profile = self.example_user('hamlet')
        email = user_profile.email
        self.login(email)

        result = self.tornado_call(get_events_backend, user_profile,
                                   {"apply_markdown": ujson.dumps(True),
                                    "event_types": ujson.dumps(["message"]),
                                    "user_client": "website",
                                    "dont_block": ujson.dumps(True),
                                    })
        self.assert_json_success(result)
        queue_id = ujson.loads(result.content)["queue_id"]
        client_descriptor = get_client_descriptor(queue_id)

        with mock.patch("zerver.tornado.event_queue.maybe_enqueue_notifications") as mock_enqueue:
            # To test the missed_message hook, we first need to send a message
            msg_id = self.send_message(self.example_email("iago"), "Denmark", Recipient.STREAM)

            # Verify that nothing happens if you call it as not the
            # "last client descriptor", in which case the function
            # short-circuits, since the `missedmessage_hook` handler
            # for garbage-collection is only for the user's last queue.
            missedmessage_hook(user_profile.id, client_descriptor, False)
            mock_enqueue.assert_not_called()

            # Now verify that we called the appropriate enqueue function
            missedmessage_hook(user_profile.id, client_descriptor, True)
            mock_enqueue.assert_called_once()
            args_list = mock_enqueue.call_args_list[0][0]

            self.assertEqual(args_list, (user_profile.id, msg_id, False, False, False, "Denmark", False, True))

        # Clear the event queue, before repeating with a private message
        client_descriptor.event_queue.pop()
        self.assertTrue(client_descriptor.event_queue.empty())
        msg_id = self.send_message(self.example_email("iago"), [email], Recipient.PERSONAL)
        with mock.patch("zerver.tornado.event_queue.maybe_enqueue_notifications") as mock_enqueue:
            missedmessage_hook(user_profile.id, client_descriptor, True)
            mock_enqueue.assert_called_once()
            args_list = mock_enqueue.call_args_list[0][0]

            self.assertEqual(args_list, (user_profile.id, msg_id, True, False, False, None, False, True))

        # Clear the event queue, now repeat with a mention
        client_descriptor.event_queue.pop()
        self.assertTrue(client_descriptor.event_queue.empty())
        msg_id = self.send_message(self.example_email("iago"), "Denmark", Recipient.STREAM,
                                   content="@**King Hamlet** what's up?")
        with mock.patch("zerver.tornado.event_queue.maybe_enqueue_notifications") as mock_enqueue:
            # Clear the event queue, before repeating with a private message
            missedmessage_hook(user_profile.id, client_descriptor, True)
            mock_enqueue.assert_called_once()
            args_list = mock_enqueue.call_args_list[0][0]

            self.assertEqual(args_list, (user_profile.id, msg_id, False, True, False, "Denmark", False, True))

        # Clear the event queue, now repeat with stream message with stream_push_notify
        stream = get_stream("Denmark", user_profile.realm)
        sub = Subscription.objects.get(user_profile=user_profile, recipient__type=Recipient.STREAM,
                                       recipient__type_id=stream.id)
        sub.push_notifications = True
        sub.save()
        client_descriptor.event_queue.pop()
        self.assertTrue(client_descriptor.event_queue.empty())
        msg_id = self.send_message(self.example_email("iago"), "Denmark", Recipient.STREAM,
                                   content="what's up everyone?")
        with mock.patch("zerver.tornado.event_queue.maybe_enqueue_notifications") as mock_enqueue:
            # Clear the event queue, before repeating with a private message
            missedmessage_hook(user_profile.id, client_descriptor, True)
            mock_enqueue.assert_called_once()
            args_list = mock_enqueue.call_args_list[0][0]

            self.assertEqual(args_list, (user_profile.id, msg_id, False, False, True, "Denmark", False, True))

        # Clean up the state
        sub.push_notifications = True
        sub.save()