Fix missing aggregated info in real-time sync race for presence.

- Add aggregated info to real-time updated presence status.
- Update `presence events` test case with adding aggregated
  information to presence event.
- Add test case for updating presence status for user which
  send state from multiple clients.

Fixes #4282.
This commit is contained in:
K.Kanakhin
2017-04-25 15:50:30 +06:00
committed by Tim Abbott
parent 088d881159
commit 18f2a7428f
4 changed files with 44 additions and 18 deletions

View File

@@ -2344,7 +2344,7 @@ def send_presence_changed(user_profile, presence):
presence_dict = presence.to_dict()
event = dict(type="presence", email=user_profile.email,
server_timestamp=time.time(),
presence={presence_dict['client']: presence.to_dict()})
presence={presence_dict['client']: presence_dict})
send_event(event, active_user_ids(user_profile.realm))
def consolidate_client(client):

View File

@@ -29,7 +29,7 @@ from zerver.lib.actions import validate_user_access_to_subscribers_helper, \
gather_subscriptions_helper, get_realm_domains, \
get_status_dict, streams_to_dicts_sorted
from zerver.tornado.event_queue import request_event_queue, get_user_events
from zerver.models import Client, Message, Realm, UserProfile, \
from zerver.models import Client, Message, Realm, UserPresence, UserProfile, \
get_user_profile_by_email, get_user_profile_by_id, \
get_active_user_dicts_in_realm, realm_filters_for_realm, \
get_owned_bot_dicts, custom_profile_fields_for_realm
@@ -349,7 +349,9 @@ def apply_event(state, event, user_profile, include_subscribers):
user_id in sub['subscribers']):
sub['subscribers'].remove(user_id)
elif event['type'] == "presence":
state['presences'][event['email']] = event['presence']
# TODO: Add user_id to presence update events / state format!
presence_user_profile = get_user_profile_by_email(event['email'])
state['presences'][event['email']] = UserPresence.get_status_dict_by_user(presence_user_profile)[event['email']]
elif event['type'] == "update_message":
# The client will get the updated message directly
pass

View File

@@ -1567,9 +1567,9 @@ class UserPresence(models.Model):
return user_statuses
@staticmethod
def to_presence_dict(client_name, status, dt, push_enabled=None,
has_push_devices=None, is_mirror_dummy=None):
# type: (Text, int, datetime.datetime, Optional[bool], Optional[bool], Optional[bool]) -> Dict[str, Any]
def to_presence_dict(client_name, status, dt, push_enabled=False,
has_push_devices=False, is_mirror_dummy=None):
# type: (Text, int, datetime.datetime, bool, bool, Optional[bool]) -> Dict[str, Any]
presence_val = UserPresence.status_to_string(status)
timestamp = datetime_to_timestamp(dt)

View File

@@ -603,23 +603,47 @@ class EventsRegisterTest(ZulipTestCase):
def test_presence_events(self):
# type: () -> None
schema_checker = self.check_events_dict([
('type', equals('pointer')),
('type', equals('presence')),
('email', check_string),
('timestamp', check_float),
('server_timestamp', check_float),
('presence', check_dict_only([
# TODO: Add more here once the test below works
('website', check_dict_only([
('status', equals('active')),
('timestamp', check_int),
('client', check_string),
('pushable', check_bool),
])),
])),
])
# BUG: Marked as failing for now because this is a failing
# test, due to the `aggregated` feature not being supported by
# our events code.
events = self.do_test(lambda: do_update_user_presence(
self.user_profile, get_client("website"), timezone_now(), UserPresence.ACTIVE))
error = schema_checker('events[0]', events[0])
self.assert_on_error(error)
with self.assertRaises(AssertionError):
events = self.do_test(lambda: do_update_user_presence(
self.user_profile, get_client("website"), timezone_now(), UserPresence.ACTIVE))
# Marked as nocoverage since unreachable
error = schema_checker('events[0]', events[0]) # nocoverage
self.assert_on_error(error) # nocoverage
def test_presence_events_multiple_clients(self):
# type: () -> None
schema_checker_android = self.check_events_dict([
('type', equals('presence')),
('email', check_string),
('server_timestamp', check_float),
('presence', check_dict_only([
('ZulipAndroid/1.0', check_dict_only([
('status', equals('idle')),
('timestamp', check_int),
('client', check_string),
('pushable', check_bool),
])),
])),
])
self.client_post("/api/v1/users/me/presence", {'status': 'idle'},
HTTP_USER_AGENT="ZulipAndroid/1.0",
**self.api_auth(self.user_profile.email))
self.do_test(lambda: do_update_user_presence(
self.user_profile, get_client("website"), timezone_now(), UserPresence.ACTIVE))
events = self.do_test(lambda: do_update_user_presence(
self.user_profile, get_client("ZulipAndroid/1.0"), timezone_now(), UserPresence.IDLE))
error = schema_checker_android('events[0]', events[0])
self.assert_on_error(error)
def test_pointer_events(self):
# type: () -> None