Newer
Older
Matthew Hodgson
committed
# Copyright 2014 matrix.org
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
Paul "LeoNerd" Evans
committed
from twisted.internet import defer, reactor
from mock import Mock, call, ANY
import logging
Paul "LeoNerd" Evans
committed
import json
Paul "LeoNerd" Evans
committed
from ..utils import MockHttpResource, MockClock, DeferredMockCallable
Paul "LeoNerd" Evans
committed
from synapse.server import HomeServer
from synapse.api.constants import PresenceState
from synapse.api.errors import SynapseError
from synapse.handlers.presence import PresenceHandler, UserPresenceCache
OFFLINE = PresenceState.OFFLINE
Paul "LeoNerd" Evans
committed
UNAVAILABLE = PresenceState.UNAVAILABLE
ONLINE = PresenceState.ONLINE
logging.getLogger().addHandler(logging.NullHandler())
Paul "LeoNerd" Evans
committed
def _expect_edu(destination, edu_type, content, origin="test"):
return {
"origin": origin,
"ts": 1000000,
"pdus": [],
"edus": [
{
"origin": origin,
"destination": destination,
"edu_type": edu_type,
"content": content,
}
],
}
Paul "LeoNerd" Evans
committed
def _make_edu_json(origin, edu_type, content):
return json.dumps(_expect_edu("test", edu_type, content, origin=origin))
class JustPresenceHandlers(object):
def __init__(self, hs):
self.presence_handler = PresenceHandler(hs)
class PresenceStateTestCase(unittest.TestCase):
""" Tests presence management. """
def setUp(self):
hs = HomeServer("test",
Paul "LeoNerd" Evans
committed
clock=MockClock(),
db_pool=None,
datastore=Mock(spec=[
"get_presence_state",
"set_presence_state",
"add_presence_list_pending",
"set_presence_list_accepted",
]),
handlers=None,
resource_for_federation=Mock(),
http_client=None,
)
hs.handlers = JustPresenceHandlers(hs)
self.datastore = hs.get_datastore()
def is_presence_visible(observed_localpart, observer_userid):
allow = (observed_localpart == "apple" and
observer_userid == "@banana:test"
)
return defer.succeed(allow)
self.datastore.is_presence_visible = is_presence_visible
# Mock the RoomMemberHandler
Paul "LeoNerd" Evans
committed
room_member_handler = Mock(spec=[])
hs.handlers.room_member_handler = room_member_handler
logging.getLogger().debug("Mocking room_member_handler=%r", room_member_handler)
# Some local users to test with
self.u_apple = hs.parse_userid("@apple:test")
self.u_banana = hs.parse_userid("@banana:test")
self.u_clementine = hs.parse_userid("@clementine:test")
self.handler = hs.get_handlers().presence_handler
self.room_members = []
def get_rooms_for_user(user):
if user in self.room_members:
return defer.succeed(["a-room"])
else:
return defer.succeed([])
room_member_handler.get_rooms_for_user = get_rooms_for_user
def get_room_members(room_id):
if room_id == "a-room":
return defer.succeed(self.room_members)
else:
return defer.succeed([])
room_member_handler.get_room_members = get_room_members
Paul "LeoNerd" Evans
committed
def user_rooms_intersect(userlist):
Paul "LeoNerd" Evans
committed
room_member_ids = map(lambda u: u.to_string(), self.room_members)
shared = all(map(lambda i: i in room_member_ids, userlist))
Paul "LeoNerd" Evans
committed
return defer.succeed(shared)
Paul "LeoNerd" Evans
committed
self.datastore.user_rooms_intersect = user_rooms_intersect
Paul "LeoNerd" Evans
committed
self.mock_start = Mock()
self.mock_stop = Mock()
self.handler.start_polling_presence = self.mock_start
self.handler.stop_polling_presence = self.mock_stop
@defer.inlineCallbacks
def test_get_my_state(self):
mocked_get = self.datastore.get_presence_state
mocked_get.return_value = defer.succeed(
{"state": ONLINE, "status_msg": "Online"}
)
state = yield self.handler.get_state(
target_user=self.u_apple, auth_user=self.u_apple
)
Paul "LeoNerd" Evans
committed
self.assertEquals(
{"state": ONLINE, "presence": ONLINE, "status_msg": "Online"},
state
)
mocked_get.assert_called_with("apple")
@defer.inlineCallbacks
def test_get_allowed_state(self):
mocked_get = self.datastore.get_presence_state
mocked_get.return_value = defer.succeed(
{"state": ONLINE, "status_msg": "Online"}
)
state = yield self.handler.get_state(
target_user=self.u_apple, auth_user=self.u_banana
)
Paul "LeoNerd" Evans
committed
self.assertEquals(
{"state": ONLINE, "presence": ONLINE, "status_msg": "Online"},
state
)
mocked_get.assert_called_with("apple")
@defer.inlineCallbacks
def test_get_same_room_state(self):
mocked_get = self.datastore.get_presence_state
mocked_get.return_value = defer.succeed(
{"state": ONLINE, "status_msg": "Online"}
)
self.room_members = [self.u_apple, self.u_clementine]
state = yield self.handler.get_state(
target_user=self.u_apple, auth_user=self.u_clementine
)
Paul "LeoNerd" Evans
committed
self.assertEquals(
{"state": ONLINE, "presence": ONLINE, "status_msg": "Online"},
state
)
@defer.inlineCallbacks
def test_get_disallowed_state(self):
mocked_get = self.datastore.get_presence_state
mocked_get.return_value = defer.succeed(
{"state": ONLINE, "status_msg": "Online"}
)
self.room_members = []
yield self.assertFailure(
self.handler.get_state(
target_user=self.u_apple, auth_user=self.u_clementine
),
SynapseError
)
@defer.inlineCallbacks
def test_set_my_state(self):
mocked_set = self.datastore.set_presence_state
mocked_set.return_value = defer.succeed({"state": OFFLINE})
yield self.handler.set_state(
target_user=self.u_apple, auth_user=self.u_apple,
Paul "LeoNerd" Evans
committed
state={"presence": UNAVAILABLE, "status_msg": "Away"})
Paul "LeoNerd" Evans
committed
{"state": UNAVAILABLE, "status_msg": "Away"})
self.mock_start.assert_called_with(self.u_apple,
Paul "LeoNerd" Evans
committed
state={
Paul "LeoNerd" Evans
committed
"presence": UNAVAILABLE,
Paul "LeoNerd" Evans
committed
"status_msg": "Away",
Paul "LeoNerd" Evans
committed
"last_active": 1000000, # MockClock
Paul "LeoNerd" Evans
committed
})
yield self.handler.set_state(
target_user=self.u_apple, auth_user=self.u_apple,
Paul "LeoNerd" Evans
committed
state={"presence": OFFLINE})
self.mock_stop.assert_called_with(self.u_apple)
class PresenceInvitesTestCase(unittest.TestCase):
""" Tests presence management. """
def setUp(self):
Paul "LeoNerd" Evans
committed
self.mock_http_client = Mock(spec=[])
self.mock_http_client.put_json = DeferredMockCallable()
self.mock_federation_resource = MockHttpResource()
Paul "LeoNerd" Evans
committed
clock=MockClock(),
db_pool=None,
datastore=Mock(spec=[
"has_presence_state",
"allow_presence_visible",
"add_presence_list_pending",
"set_presence_list_accepted",
"get_presence_list",
"del_presence_list",
Paul "LeoNerd" Evans
committed
# Bits that Federation needs
"prep_send_transaction",
"delivered_txn",
"get_received_txn_response",
"set_received_txn_response",
resource_for_client=Mock(),
Paul "LeoNerd" Evans
committed
resource_for_federation=self.mock_federation_resource,
http_client=self.mock_http_client,
)
hs.handlers = JustPresenceHandlers(hs)
self.datastore = hs.get_datastore()
def has_presence_state(user_localpart):
return defer.succeed(
user_localpart in ("apple", "banana"))
self.datastore.has_presence_state = has_presence_state
Paul "LeoNerd" Evans
committed
def get_received_txn_response(*args):
return defer.succeed(None)
self.datastore.get_received_txn_response = get_received_txn_response
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
# Some local users to test with
self.u_apple = hs.parse_userid("@apple:test")
self.u_banana = hs.parse_userid("@banana:test")
# ID of a local user that does not exist
self.u_durian = hs.parse_userid("@durian:test")
# A remote user
self.u_cabbage = hs.parse_userid("@cabbage:elsewhere")
self.handler = hs.get_handlers().presence_handler
self.mock_start = Mock()
self.mock_stop = Mock()
self.handler.start_polling_presence = self.mock_start
self.handler.stop_polling_presence = self.mock_stop
@defer.inlineCallbacks
def test_invite_local(self):
# TODO(paul): This test will likely break if/when real auth permissions
# are added; for now the HS will always accept any invite
yield self.handler.send_invite(
observer_user=self.u_apple, observed_user=self.u_banana)
self.datastore.add_presence_list_pending.assert_called_with(
"apple", "@banana:test")
self.datastore.allow_presence_visible.assert_called_with(
"banana", "@apple:test")
self.datastore.set_presence_list_accepted.assert_called_with(
"apple", "@banana:test")
self.mock_start.assert_called_with(
self.u_apple, target_user=self.u_banana)
@defer.inlineCallbacks
def test_invite_local_nonexistant(self):
yield self.handler.send_invite(
observer_user=self.u_apple, observed_user=self.u_durian)
self.datastore.add_presence_list_pending.assert_called_with(
"apple", "@durian:test")
self.datastore.del_presence_list.assert_called_with(
"apple", "@durian:test")
@defer.inlineCallbacks
def test_invite_remote(self):
Paul "LeoNerd" Evans
committed
put_json = self.mock_http_client.put_json
put_json.expect_call_and_return(
call("elsewhere",
Matthew Hodgson
committed
path="/_matrix/federation/v1/send/1000000/",
Paul "LeoNerd" Evans
committed
data=_expect_edu("elsewhere", "m.presence_invite",
content={
"observer_user": "@apple:test",
"observed_user": "@cabbage:elsewhere",
}
)
),
defer.succeed((200, "OK"))
)
yield self.handler.send_invite(
observer_user=self.u_apple, observed_user=self.u_cabbage)
self.datastore.add_presence_list_pending.assert_called_with(
"apple", "@cabbage:elsewhere")
Paul "LeoNerd" Evans
committed
yield put_json.await_calls()
@defer.inlineCallbacks
def test_accept_remote(self):
# TODO(paul): This test will likely break if/when real auth permissions
# are added; for now the HS will always accept any invite
Paul "LeoNerd" Evans
committed
put_json = self.mock_http_client.put_json
put_json.expect_call_and_return(
call("elsewhere",
Matthew Hodgson
committed
path="/_matrix/federation/v1/send/1000000/",
Paul "LeoNerd" Evans
committed
data=_expect_edu("elsewhere", "m.presence_accept",
content={
"observer_user": "@cabbage:elsewhere",
"observed_user": "@apple:test",
}
)
),
defer.succeed((200, "OK"))
)
Paul "LeoNerd" Evans
committed
yield self.mock_federation_resource.trigger("PUT",
Matthew Hodgson
committed
"/_matrix/federation/v1/send/1000000/",
Paul "LeoNerd" Evans
committed
_make_edu_json("elsewhere", "m.presence_invite",
content={
"observer_user": "@cabbage:elsewhere",
"observed_user": "@apple:test",
}
Paul "LeoNerd" Evans
committed
)
)
self.datastore.allow_presence_visible.assert_called_with(
"apple", "@cabbage:elsewhere")
Paul "LeoNerd" Evans
committed
yield put_json.await_calls()
@defer.inlineCallbacks
def test_invited_remote_nonexistant(self):
Paul "LeoNerd" Evans
committed
put_json = self.mock_http_client.put_json
put_json.expect_call_and_return(
call("elsewhere",
Matthew Hodgson
committed
path="/_matrix/federation/v1/send/1000000/",
Paul "LeoNerd" Evans
committed
data=_expect_edu("elsewhere", "m.presence_deny",
content={
"observer_user": "@cabbage:elsewhere",
"observed_user": "@durian:test",
}
)
),
defer.succeed((200, "OK"))
Paul "LeoNerd" Evans
committed
yield self.mock_federation_resource.trigger("PUT",
Matthew Hodgson
committed
"/_matrix/federation/v1/send/1000000/",
Paul "LeoNerd" Evans
committed
_make_edu_json("elsewhere", "m.presence_invite",
content={
"observer_user": "@cabbage:elsewhere",
"observed_user": "@durian:test",
}
Paul "LeoNerd" Evans
committed
)
Paul "LeoNerd" Evans
committed
yield put_json.await_calls()
@defer.inlineCallbacks
def test_accepted_remote(self):
Paul "LeoNerd" Evans
committed
yield self.mock_federation_resource.trigger("PUT",
Matthew Hodgson
committed
"/_matrix/federation/v1/send/1000000/",
Paul "LeoNerd" Evans
committed
_make_edu_json("elsewhere", "m.presence_accept",
content={
"observer_user": "@apple:test",
"observed_user": "@cabbage:elsewhere",
}
Paul "LeoNerd" Evans
committed
)
)
self.datastore.set_presence_list_accepted.assert_called_with(
"apple", "@cabbage:elsewhere")
self.mock_start.assert_called_with(
self.u_apple, target_user=self.u_cabbage)
@defer.inlineCallbacks
def test_denied_remote(self):
Paul "LeoNerd" Evans
committed
yield self.mock_federation_resource.trigger("PUT",
Matthew Hodgson
committed
"/_matrix/federation/v1/send/1000000/",
Paul "LeoNerd" Evans
committed
_make_edu_json("elsewhere", "m.presence_deny",
content={
"observer_user": "@apple:test",
"observed_user": "@eggplant:elsewhere",
}
Paul "LeoNerd" Evans
committed
)
)
self.datastore.del_presence_list.assert_called_with(
"apple", "@eggplant:elsewhere")
@defer.inlineCallbacks
def test_drop_local(self):
yield self.handler.drop(
observer_user=self.u_apple, observed_user=self.u_banana)
self.datastore.del_presence_list.assert_called_with(
"apple", "@banana:test")
self.mock_stop.assert_called_with(
self.u_apple, target_user=self.u_banana)
@defer.inlineCallbacks
def test_drop_remote(self):
yield self.handler.drop(
observer_user=self.u_apple, observed_user=self.u_cabbage)
self.datastore.del_presence_list.assert_called_with(
"apple", "@cabbage:elsewhere")
@defer.inlineCallbacks
def test_get_presence_list(self):
self.datastore.get_presence_list.return_value = defer.succeed(
Paul "LeoNerd" Evans
committed
[{"observed_user_id": "@banana:test"}]
)
presence = yield self.handler.get_presence_list(
observer_user=self.u_apple)
Paul "LeoNerd" Evans
committed
self.assertEquals([
{"observed_user": self.u_banana,
"presence": OFFLINE,
"state": OFFLINE},
], presence)
self.datastore.get_presence_list.assert_called_with("apple",
Paul "LeoNerd" Evans
committed
accepted=None
)
self.datastore.get_presence_list.return_value = defer.succeed(
Paul "LeoNerd" Evans
committed
[{"observed_user_id": "@banana:test"}]
)
presence = yield self.handler.get_presence_list(
Paul "LeoNerd" Evans
committed
observer_user=self.u_apple, accepted=True
)
Paul "LeoNerd" Evans
committed
self.assertEquals([
{"observed_user": self.u_banana,
"presence": OFFLINE,
"state": OFFLINE},
], presence)
self.datastore.get_presence_list.assert_called_with("apple",
accepted=True)
class PresencePushTestCase(unittest.TestCase):
""" Tests steady-state presence status updates.
They assert that presence state update messages are pushed around the place
when users change state, presuming that the watches are all established.
These tests are MASSIVELY fragile currently as they poke internals of the
presence handler; namely the _local_pushmap and _remote_recvmap.
BE WARNED...
"""
def setUp(self):
Paul "LeoNerd" Evans
committed
self.clock = MockClock()
Paul "LeoNerd" Evans
committed
self.mock_http_client = Mock(spec=[])
self.mock_http_client.put_json = DeferredMockCallable()
self.mock_federation_resource = MockHttpResource()
Paul "LeoNerd" Evans
committed
clock=self.clock,
db_pool=None,
datastore=Mock(spec=[
"set_presence_state",
Erik Johnston
committed
"get_joined_hosts_for_room",
Paul "LeoNerd" Evans
committed
# Bits that Federation needs
"prep_send_transaction",
"delivered_txn",
"get_received_txn_response",
"set_received_txn_response",
resource_for_client=Mock(),
Paul "LeoNerd" Evans
committed
resource_for_federation=self.mock_federation_resource,
http_client=self.mock_http_client,
)
hs.handlers = JustPresenceHandlers(hs)
self.datastore = hs.get_datastore()
Paul "LeoNerd" Evans
committed
def get_received_txn_response(*args):
return defer.succeed(None)
self.datastore.get_received_txn_response = get_received_txn_response
self.handler = hs.get_handlers().presence_handler
self.event_source = hs.get_event_sources().sources["presence"]
# Mock the RoomMemberHandler
hs.handlers.room_member_handler = Mock(spec=[
"get_rooms_for_user",
"get_room_members",
])
self.room_member_handler = hs.handlers.room_member_handler
self.room_members = []
def get_rooms_for_user(user):
if user in self.room_members:
return defer.succeed(["a-room"])
else:
return defer.succeed([])
self.room_member_handler.get_rooms_for_user = get_rooms_for_user
def get_room_members(room_id):
if room_id == "a-room":
return defer.succeed(self.room_members)
else:
return defer.succeed([])
self.room_member_handler.get_room_members = get_room_members
Erik Johnston
committed
def get_room_hosts(room_id):
if room_id == "a-room":
hosts = set([u.domain for u in self.room_members])
return defer.succeed(hosts)
else:
return defer.succeed([])
self.datastore.get_joined_hosts_for_room = get_room_hosts
Paul "LeoNerd" Evans
committed
def user_rooms_intersect(userlist):
room_member_ids = map(lambda u: u.to_string(), self.room_members)
shared = all(map(lambda i: i in room_member_ids, userlist))
return defer.succeed(shared)
self.datastore.user_rooms_intersect = user_rooms_intersect
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
@defer.inlineCallbacks
def fetch_room_distributions_into(room_id, localusers=None,
remotedomains=None, ignore_user=None):
members = yield get_room_members(room_id)
for member in members:
if ignore_user is not None and member == ignore_user:
continue
if member.is_mine:
if localusers is not None:
localusers.add(member)
else:
if remotedomains is not None:
remotedomains.add(member.domain)
self.room_member_handler.fetch_room_distributions_into = (
fetch_room_distributions_into)
def get_presence_list(user_localpart, accepted=None):
if user_localpart == "apple":
return defer.succeed([
{"observed_user_id": "@banana:test"},
{"observed_user_id": "@clementine:test"},
])
else:
return defer.succeed([])
self.datastore.get_presence_list = get_presence_list
def is_presence_visible(observer_userid, observed_localpart):
if (observed_localpart == "clementine" and
observer_userid == "@banana:test"):
return False
return False
self.datastore.is_presence_visible = is_presence_visible
self.distributor = hs.get_distributor()
self.distributor.declare("user_joined_room")
# Some local users to test with
self.u_apple = hs.parse_userid("@apple:test")
self.u_banana = hs.parse_userid("@banana:test")
self.u_clementine = hs.parse_userid("@clementine:test")
Paul "LeoNerd" Evans
committed
self.u_durian = hs.parse_userid("@durian:test")
self.u_elderberry = hs.parse_userid("@elderberry:test")
# Remote user
self.u_onion = hs.parse_userid("@onion:farm")
self.u_potato = hs.parse_userid("@potato:remote")
@defer.inlineCallbacks
def test_push_local(self):
self.room_members = [self.u_apple, self.u_elderberry]
self.datastore.set_presence_state.return_value = defer.succeed(
{"state": ONLINE})
# TODO(paul): Gut-wrenching
self.handler._user_cachemap[self.u_apple] = UserPresenceCache()
Paul "LeoNerd" Evans
committed
self.handler._user_cachemap[self.u_apple].update(
{"presence": OFFLINE}, serial=0
)
apple_set = self.handler._local_pushmap.setdefault("apple", set())
apple_set.add(self.u_banana)
apple_set.add(self.u_clementine)
self.assertEquals(self.event_source.get_current_key(), 0)
yield self.handler.set_state(self.u_apple, self.u_apple,
Paul "LeoNerd" Evans
committed
{"presence": ONLINE}
)
Paul "LeoNerd" Evans
committed
# Apple sees self-reflection
Paul "LeoNerd" Evans
committed
(events, _) = yield self.event_source.get_new_events_for_user(
self.u_apple, 0, None
)
self.assertEquals(self.event_source.get_current_key(), 1)
Paul "LeoNerd" Evans
committed
self.assertEquals(events,
[
{"type": "m.presence",
"content": {
"user_id": "@apple:test",
Paul "LeoNerd" Evans
committed
"presence": ONLINE,
Paul "LeoNerd" Evans
committed
"last_active_ago": 0,
Paul "LeoNerd" Evans
committed
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
msg="Presence event should be visible to self-reflection"
)
# Banana sees it because of presence subscription
(events, _) = yield self.event_source.get_new_events_for_user(
self.u_banana, 0, None
)
self.assertEquals(self.event_source.get_current_key(), 1)
self.assertEquals(events,
[
{"type": "m.presence",
"content": {
"user_id": "@apple:test",
"presence": ONLINE,
"state": ONLINE,
"last_active_ago": 0,
}},
],
msg="Presence event should be visible to explicit subscribers"
)
# Elderberry sees it because of same room
(events, _) = yield self.event_source.get_new_events_for_user(
self.u_elderberry, 0, None
)
self.assertEquals(self.event_source.get_current_key(), 1)
self.assertEquals(events,
[
{"type": "m.presence",
"content": {
"user_id": "@apple:test",
"presence": ONLINE,
"state": ONLINE,
"last_active_ago": 0,
}},
],
msg="Presence event should be visible to other room members"
)
# Durian is not in the room, should not see this event
(events, _) = yield self.event_source.get_new_events_for_user(
self.u_durian, 0, None
)
self.assertEquals(self.event_source.get_current_key(), 1)
self.assertEquals(events, [],
msg="Presence event should not be visible to others"
presence = yield self.handler.get_presence_list(
observer_user=self.u_apple, accepted=True)
Paul "LeoNerd" Evans
committed
self.assertEquals(
[
{"observed_user": self.u_banana,
"presence": OFFLINE,
"state": OFFLINE},
{"observed_user": self.u_clementine,
"presence": OFFLINE,
"state": OFFLINE},
],
presence
)
Paul "LeoNerd" Evans
committed
# TODO(paul): Gut-wrenching
banana_set = self.handler._local_pushmap.setdefault("banana", set())
banana_set.add(self.u_apple)
yield self.handler.set_state(self.u_banana, self.u_banana,
Paul "LeoNerd" Evans
committed
{"presence": ONLINE}
)
Paul "LeoNerd" Evans
committed
self.clock.advance_time(2)
presence = yield self.handler.get_presence_list(
observer_user=self.u_apple, accepted=True)
self.assertEquals([
Paul "LeoNerd" Evans
committed
{"observed_user": self.u_banana,
Paul "LeoNerd" Evans
committed
"presence": ONLINE,
Paul "LeoNerd" Evans
committed
"state": ONLINE,
Paul "LeoNerd" Evans
committed
"last_active_ago": 2000},
Paul "LeoNerd" Evans
committed
{"observed_user": self.u_clementine,
Paul "LeoNerd" Evans
committed
"presence": OFFLINE,
Paul "LeoNerd" Evans
committed
"state": OFFLINE},
], presence)
Paul "LeoNerd" Evans
committed
(events, _) = yield self.event_source.get_new_events_for_user(
self.u_apple, 1, None
)
self.assertEquals(self.event_source.get_current_key(), 2)
Paul "LeoNerd" Evans
committed
self.assertEquals(events,
[
{"type": "m.presence",
"content": {
"user_id": "@banana:test",
Paul "LeoNerd" Evans
committed
"presence": ONLINE,
Paul "LeoNerd" Evans
committed
"last_active_ago": 2000
@defer.inlineCallbacks
def test_push_remote(self):
Paul "LeoNerd" Evans
committed
put_json = self.mock_http_client.put_json
put_json.expect_call_and_return(
call("farm",
path=ANY, # Can't guarantee which txn ID will be which
data=_expect_edu("farm", "m.presence",
content={
"push": [
{"user_id": "@apple:test",
Paul "LeoNerd" Evans
committed
"presence": u"online",
Erik Johnston
committed
"state": u"online",
Paul "LeoNerd" Evans
committed
"last_active_ago": 0},
Paul "LeoNerd" Evans
committed
}
)
),
defer.succeed((200, "OK"))
)
Paul "LeoNerd" Evans
committed
put_json.expect_call_and_return(
call("remote",
path=ANY, # Can't guarantee which txn ID will be which
data=_expect_edu("remote", "m.presence",
content={
"push": [
{"user_id": "@apple:test",
"presence": u"online",
"state": u"online",
"last_active_ago": 0},
],
}
)
),
defer.succeed((200, "OK"))
)
Paul "LeoNerd" Evans
committed
self.room_members = [self.u_apple, self.u_onion]
self.datastore.set_presence_state.return_value = defer.succeed(
{"state": ONLINE}
)
# TODO(paul): Gut-wrenching
self.handler._user_cachemap[self.u_apple] = UserPresenceCache()
Paul "LeoNerd" Evans
committed
self.handler._user_cachemap[self.u_apple].update(
{"presence": OFFLINE}, serial=0
)
Paul "LeoNerd" Evans
committed
apple_set = self.handler._remote_sendmap.setdefault("apple", set())
apple_set.add(self.u_potato.domain)
yield self.handler.set_state(self.u_apple, self.u_apple,
Paul "LeoNerd" Evans
committed
{"presence": ONLINE}
Paul "LeoNerd" Evans
committed
)
yield put_json.await_calls()
@defer.inlineCallbacks
def test_recv_remote(self):
# TODO(paul): Gut-wrenching
potato_set = self.handler._remote_recvmap.setdefault(self.u_potato,
set())
potato_set.add(self.u_apple)
self.room_members = [self.u_banana, self.u_potato]
self.assertEquals(self.event_source.get_current_key(), 0)
Paul "LeoNerd" Evans
committed
yield self.mock_federation_resource.trigger("PUT",
Matthew Hodgson
committed
"/_matrix/federation/v1/send/1000000/",
Paul "LeoNerd" Evans
committed
_make_edu_json("elsewhere", "m.presence",
content={
Paul "LeoNerd" Evans
committed
"state": "online",
Paul "LeoNerd" Evans
committed
"last_active_ago": 1000},
Paul "LeoNerd" Evans
committed
)
Paul "LeoNerd" Evans
committed
(events, _) = yield self.event_source.get_new_events_for_user(
self.u_apple, 0, None
)
self.assertEquals(self.event_source.get_current_key(), 1)
Paul "LeoNerd" Evans
committed
self.assertEquals(events,
[
{"type": "m.presence",
"content": {
"user_id": "@potato:remote",
Paul "LeoNerd" Evans
committed
"presence": ONLINE,
Paul "LeoNerd" Evans
committed
"last_active_ago": 1000,
Paul "LeoNerd" Evans
committed
self.clock.advance_time(2)
state = yield self.handler.get_state(self.u_potato, self.u_apple)
Paul "LeoNerd" Evans
committed
self.assertEquals(
Paul "LeoNerd" Evans
committed
{"state": ONLINE, "presence": ONLINE, "last_active_ago": 3000},
Paul "LeoNerd" Evans
committed
state
)
@defer.inlineCallbacks
def test_join_room_local(self):
self.room_members = [self.u_apple, self.u_banana]
self.assertEquals(self.event_source.get_current_key(), 0)
# TODO(paul): Gut-wrenching
self.handler._user_cachemap[self.u_clementine] = UserPresenceCache()
self.handler._user_cachemap[self.u_clementine].update(
{
Paul "LeoNerd" Evans
committed
"presence": PresenceState.ONLINE,
Paul "LeoNerd" Evans
committed
"last_active": self.clock.time_msec(),
}, self.u_clementine
)
yield self.distributor.fire("user_joined_room", self.u_clementine,
Paul "LeoNerd" Evans
committed
self.room_members.append(self.u_clementine)
Paul "LeoNerd" Evans
committed
(events, _) = yield self.event_source.get_new_events_for_user(
self.u_apple, 0, None
)
self.assertEquals(self.event_source.get_current_key(), 1)
Paul "LeoNerd" Evans
committed
self.assertEquals(events,
[
{"type": "m.presence",
"content": {
"user_id": "@clementine:test",
Paul "LeoNerd" Evans
committed
"presence": ONLINE,
Paul "LeoNerd" Evans
committed
"last_active_ago": 0,
@defer.inlineCallbacks
def test_join_room_remote(self):
## Sending local user state to a newly-joined remote user
Paul "LeoNerd" Evans
committed
put_json = self.mock_http_client.put_json
put_json.expect_call_and_return(
call("remote",
path=ANY, # Can't guarantee which txn ID will be which
data=_expect_edu("remote", "m.presence",
content={
"push": [
{"user_id": "@apple:test",
Paul "LeoNerd" Evans
committed
"presence": "online",
"state": "online"},
Paul "LeoNerd" Evans
committed
],
}
),
),
defer.succeed((200, "OK"))
)
put_json.expect_call_and_return(
call("remote",
path=ANY, # Can't guarantee which txn ID will be which
data=_expect_edu("remote", "m.presence",
content={
"push": [
{"user_id": "@banana:test",
Paul "LeoNerd" Evans
committed
"presence": "offline",
"state": "offline"},
Paul "LeoNerd" Evans
committed
],
}
),
),
defer.succeed((200, "OK"))
)
# TODO(paul): Gut-wrenching
self.handler._user_cachemap[self.u_apple] = UserPresenceCache()
self.handler._user_cachemap[self.u_apple].update(
Paul "LeoNerd" Evans
committed
{"presence": PresenceState.ONLINE}, self.u_apple)
self.room_members = [self.u_apple, self.u_banana]
yield self.distributor.fire("user_joined_room", self.u_potato,
"a-room"
)
Paul "LeoNerd" Evans
committed
yield put_json.await_calls()
## Sending newly-joined local user state to remote users
put_json.expect_call_and_return(
call("remote",
Matthew Hodgson
committed
path="/_matrix/federation/v1/send/1000002/",
Paul "LeoNerd" Evans
committed
data=_expect_edu("remote", "m.presence",
Paul "LeoNerd" Evans
committed
{"user_id": "@clementine:test",
Paul "LeoNerd" Evans
committed
"presence": "online",
"state": "online"},
Paul "LeoNerd" Evans
committed
}
),
),
defer.succeed((200, "OK"))
)
self.handler._user_cachemap[self.u_clementine] = UserPresenceCache()
self.handler._user_cachemap[self.u_clementine].update(
Paul "LeoNerd" Evans
committed
{"presence": ONLINE}, self.u_clementine)
self.room_members.append(self.u_potato)
yield self.distributor.fire("user_joined_room", self.u_clementine,
"a-room"
)
Paul "LeoNerd" Evans
committed
put_json.await_calls()
class PresencePollingTestCase(unittest.TestCase):
""" Tests presence status polling. """
# For this test, we have three local users; apple is watching and is
# watched by the other two, but the others don't watch each other.
# Additionally clementine is watching a remote user.
PRESENCE_LIST = {
'apple': [ "@banana:test", "@clementine:test" ],
'banana': [ "@apple:test" ],
'clementine': [ "@apple:test", "@potato:remote" ],
Paul "LeoNerd" Evans
committed
'fig': [ "@potato:remote" ],