Example #1
0
    def setUp(self):
        self.clock = MockClock()
        self.hs = Mock()
        self.hs.get_clock = Mock(return_value=self.clock)
        self.hs.get_auth = Mock()
        self.cache = HttpTransactionCache(self.hs)

        self.mock_http_response = (200, "GOOD JOB!")
        self.mock_key = "foo"
Example #2
0
class HttpTransactionCacheTestCase(unittest.TestCase):
    def setUp(self):
        self.clock = MockClock()
        self.cache = HttpTransactionCache(self.clock)

        self.mock_http_response = (200, "GOOD JOB!")
        self.mock_key = "foo"

    @defer.inlineCallbacks
    def test_executes_given_function(self):
        cb = Mock(return_value=defer.succeed(self.mock_http_response))
        res = yield self.cache.fetch_or_execute(self.mock_key,
                                                cb,
                                                "some_arg",
                                                keyword="arg")
        cb.assert_called_once_with("some_arg", keyword="arg")
        self.assertEqual(res, self.mock_http_response)

    @defer.inlineCallbacks
    def test_deduplicates_based_on_key(self):
        cb = Mock(return_value=defer.succeed(self.mock_http_response))
        for i in range(3):  # invoke multiple times
            res = yield self.cache.fetch_or_execute(self.mock_key,
                                                    cb,
                                                    "some_arg",
                                                    keyword="arg",
                                                    changing_args=i)
            self.assertEqual(res, self.mock_http_response)
        # expect only a single call to do the work
        cb.assert_called_once_with("some_arg", keyword="arg", changing_args=0)

    @defer.inlineCallbacks
    def test_cleans_up(self):
        cb = Mock(return_value=defer.succeed(self.mock_http_response))
        yield self.cache.fetch_or_execute(self.mock_key, cb, "an arg")
        # should NOT have cleaned up yet
        self.clock.advance_time_msec(CLEANUP_PERIOD_MS / 2)

        yield self.cache.fetch_or_execute(self.mock_key, cb, "an arg")
        # still using cache
        cb.assert_called_once_with("an arg")

        self.clock.advance_time_msec(CLEANUP_PERIOD_MS)

        yield self.cache.fetch_or_execute(self.mock_key, cb, "an arg")
        # no longer using cache
        self.assertEqual(cb.call_count, 2)
        self.assertEqual(cb.call_args_list,
                         [call("an arg", ), call("an arg", )])
Example #3
0
    def test_from_cache(self):
        clock = MockClock()

        dns_client_mock = Mock(spec_set=["lookupService"])
        dns_client_mock.lookupService = Mock(spec_set=[])

        service_name = b"test_service.example.com"

        entry = Mock(spec_set=["expires", "priority", "weight"])
        entry.expires = 999999999
        entry.priority = 0
        entry.weight = 0

        cache = {service_name: [entry]}
        resolver = SrvResolver(dns_client=dns_client_mock,
                               cache=cache,
                               get_time=clock.time)

        servers = yield defer.ensureDeferred(
            resolver.resolve_service(service_name))

        self.assertFalse(dns_client_mock.lookupService.called)

        self.assertEquals(len(servers), 1)
        self.assertEquals(servers, cache[service_name])
Example #4
0
    def setUp(self):
        self.clock = MockClock()

        self.mock_federation_resource = MockHttpResource()

        self.mock_http_client = Mock(spec=[])
        self.mock_http_client.put_json = DeferredMockCallable()

        hs_kwargs = {}
        if hasattr(self, "make_datastore_mock"):
            hs_kwargs["datastore"] = self.make_datastore_mock()

        hs = yield setup_test_homeserver(
            clock=self.clock,
            handlers=None,
            resource_for_federation=self.mock_federation_resource,
            http_client=self.mock_http_client,
            keyring=Mock(),
            **hs_kwargs)
        hs.handlers = JustPresenceHandlers(hs)

        self.datastore = hs.get_datastore()

        self.setUp_roommemberhandler_mocks(hs.handlers)

        self.handler = hs.get_handlers().presence_handler
        self.event_source = hs.get_event_sources().sources["presence"]

        self.distributor = hs.get_distributor()
        self.distributor.declare("user_joined_room")

        yield self.setUp_users(hs)
Example #5
0
    def test_cache_expiry(self) -> None:
        """Test that URL cache files and thumbnails are cleaned up properly on expiry."""
        self.preview_url.clock = MockClock()

        _host, media_id = self._download_image()

        file_path = self.preview_url.filepaths.url_cache_filepath(media_id)
        file_dirs = self.preview_url.filepaths.url_cache_filepath_dirs_to_delete(
            media_id)
        thumbnail_dir = self.preview_url.filepaths.url_cache_thumbnail_directory(
            media_id)
        thumbnail_dirs = self.preview_url.filepaths.url_cache_thumbnail_dirs_to_delete(
            media_id)

        self.assertTrue(os.path.isfile(file_path))
        self.assertTrue(os.path.isdir(thumbnail_dir))

        self.preview_url.clock.advance_time_msec(IMAGE_CACHE_EXPIRY_MS + 1)
        self.get_success(self.preview_url._expire_url_cache_data())

        for path in [file_path] + file_dirs + [thumbnail_dir] + thumbnail_dirs:
            self.assertFalse(
                os.path.exists(path),
                f"{os.path.relpath(path, self.media_store_path)} was not deleted",
            )
    def setUp(self):
        hs = yield setup_test_homeserver(clock=MockClock())

        self.store = PresenceStore(hs)

        self.u_apple = UserID.from_string("@apple:test")
        self.u_banana = UserID.from_string("@banana:test")
    def test_get_set(self):
        clock = MockClock()
        cache = ExpiringCache("test", clock, max_len=1)

        cache["key"] = "value"
        self.assertEquals(cache.get("key"), "value")
        self.assertEquals(cache["key"], "value")
Example #8
0
    def setUp(self):
        self.clock = MockClock()
        self.hs = Mock()
        self.hs.get_clock = Mock(return_value=self.clock)
        self.hs.get_auth = Mock()
        self.cache = HttpTransactionCache(self.hs)

        self.mock_http_response = (200, "GOOD JOB!")
        self.mock_key = "foo"
Example #9
0
 def setUp(self):
     self.mock_store = Mock()
     self.mock_as_api = Mock()
     self.mock_scheduler = Mock()
     hs = Mock()
     hs.get_datastore = Mock(return_value=self.mock_store)
     hs.get_application_service_api = Mock(return_value=self.mock_as_api)
     hs.get_application_service_scheduler = Mock(return_value=self.mock_scheduler)
     hs.get_clock.return_value = MockClock()
     self.handler = ApplicationServicesHandler(hs)
Example #10
0
 def setUp(self):
     self.mock_store = Mock()
     self.mock_as_api = Mock()
     self.mock_scheduler = Mock()
     hs = Mock()
     hs.get_datastore.return_value = self.mock_store
     self.mock_store.get_received_ts.return_value = make_awaitable(0)
     self.mock_store.set_appservice_last_pos.return_value = make_awaitable(None)
     hs.get_application_service_api.return_value = self.mock_as_api
     hs.get_application_service_scheduler.return_value = self.mock_scheduler
     hs.get_clock.return_value = MockClock()
     self.handler = ApplicationServicesHandler(hs)
    def test_eviction(self):
        clock = MockClock()
        cache = ExpiringCache("test", clock, max_len=2)

        cache["key"] = "value"
        cache["key2"] = "value2"
        self.assertEquals(cache.get("key"), "value")
        self.assertEquals(cache.get("key2"), "value2")

        cache["key3"] = "value3"
        self.assertEquals(cache.get("key"), None)
        self.assertEquals(cache.get("key2"), "value2")
        self.assertEquals(cache.get("key3"), "value3")
Example #12
0
    def setUp(self):
        db_pool = SQLiteMemoryDbPool()
        yield db_pool.prepare()

        hs = HomeServer(
            "test",
            clock=MockClock(),
            db_pool=db_pool,
        )

        self.store = PresenceStore(hs)

        self.u_apple = hs.parse_userid("@apple:test")
        self.u_banana = hs.parse_userid("@banana:test")
Example #13
0
 def setUp(self):
     db_pool = SQLiteMemoryDbPool()
     yield db_pool.prepare()
     hs = HomeServer("test",
                     db_pool=db_pool,
                     clock=MockClock(),
                     config=Mock())
     self.as_token = "token1"
     db_pool.runQuery("INSERT INTO application_services(token) VALUES(?)",
                      (self.as_token, ))
     db_pool.runQuery("INSERT INTO application_services(token) VALUES(?)",
                      ("token2", ))
     db_pool.runQuery("INSERT INTO application_services(token) VALUES(?)",
                      ("token3", ))
     # must be done after inserts
     self.store = ApplicationServiceStore(hs)
Example #14
0
    def test_time_eviction(self):
        clock = MockClock()
        cache = ExpiringCache("test", clock, expiry_ms=1000)

        cache["key"] = 1
        clock.advance_time(0.5)
        cache["key2"] = 2

        self.assertEqual(cache.get("key"), 1)
        self.assertEqual(cache.get("key2"), 2)

        clock.advance_time(0.9)
        self.assertEqual(cache.get("key"), None)
        self.assertEqual(cache.get("key2"), 2)

        clock.advance_time(1)
        self.assertEqual(cache.get("key"), None)
        self.assertEqual(cache.get("key2"), None)
    def test_iterable_eviction(self):
        clock = MockClock()
        cache = ExpiringCache("test", clock, max_len=5, iterable=True)

        cache["key"] = [1]
        cache["key2"] = [2, 3]
        cache["key3"] = [4, 5]

        self.assertEquals(cache.get("key"), [1])
        self.assertEquals(cache.get("key2"), [2, 3])
        self.assertEquals(cache.get("key3"), [4, 5])

        cache["key4"] = [6, 7]
        self.assertEquals(cache.get("key"), None)
        self.assertEquals(cache.get("key2"), None)
        self.assertEquals(cache.get("key3"), [4, 5])
        self.assertEquals(cache.get("key4"), [6, 7])
Example #16
0
    def setUp(self):
        self.mock_http_client = Mock(spec=[])
        self.mock_http_client.put_json = DeferredMockCallable()

        self.mock_federation_resource = MockHttpResource()

        db_pool = SQLiteMemoryDbPool()
        yield db_pool.prepare()

        self.mock_config = NonCallableMock()
        self.mock_config.signing_key = [MockKey()]

        hs = HomeServer(
            "test",
            clock=MockClock(),
            db_pool=db_pool,
            handlers=None,
            resource_for_client=Mock(),
            resource_for_federation=self.mock_federation_resource,
            http_client=self.mock_http_client,
            config=self.mock_config,
            keyring=Mock(),
        )
        hs.handlers = JustPresenceHandlers(hs)

        self.store = hs.get_datastore()

        # Some local users to test with
        self.u_apple = hs.parse_userid("@apple:test")
        self.u_banana = hs.parse_userid("@banana:test")
        yield self.store.create_presence(self.u_apple.localpart)
        yield self.store.create_presence(self.u_banana.localpart)

        # 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
Example #17
0
    def test_time_eviction(self):
        clock = MockClock()
        cache = ExpiringCache("test", clock, expiry_ms=1000)

        cache["key"] = 1
        clock.advance_time(0.5)
        cache["key2"] = 2

        self.assertEquals(cache.get("key"), 1)
        self.assertEquals(cache.get("key2"), 2)

        clock.advance_time(0.9)
        self.assertEquals(cache.get("key"), None)
        self.assertEquals(cache.get("key2"), 2)

        clock.advance_time(1)
        self.assertEquals(cache.get("key"), None)
        self.assertEquals(cache.get("key2"), None)
Example #18
0
    def test_from_cache(self):
        clock = MockClock()

        dns_client_mock = Mock(spec_set=['lookupService'])
        dns_client_mock.lookupService = Mock(spec_set=[])

        service_name = "test_service.example.com"

        entry = Mock(spec_set=["expires"])
        entry.expires = 999999999

        cache = {service_name: [entry]}

        servers = yield resolve_service(service_name,
                                        dns_client=dns_client_mock,
                                        cache=cache,
                                        clock=clock)

        self.assertFalse(dns_client_mock.lookupService.called)

        self.assertEquals(len(servers), 1)
        self.assertEquals(servers, cache[service_name])
Example #19
0
 def make_homeserver(self, reactor: MemoryReactor, clock: Clock) -> HomeServer:
     # We need to be able to test advancing time in the homeserver, so we
     # replace the test homeserver's default clock with a MockClock, which
     # supports advancing time.
     return self.setup_test_homeserver(clock=MockClock())
Example #20
0
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):
        self.clock = MockClock()

        self.mock_http_client = Mock(spec=[])
        self.mock_http_client.put_json = DeferredMockCallable()

        self.mock_federation_resource = MockHttpResource()

        hs = HomeServer(
            "test",
            clock=self.clock,
            db_pool=None,
            datastore=Mock(spec=[
                "set_presence_state",
                "get_joined_hosts_for_room",

                # Bits that Federation needs
                "prep_send_transaction",
                "delivered_txn",
                "get_received_txn_response",
                "set_received_txn_response",
            ]),
            handlers=None,
            resource_for_client=Mock(),
            resource_for_federation=self.mock_federation_resource,
            http_client=self.mock_http_client,
        )
        hs.handlers = JustPresenceHandlers(hs)

        self.datastore = hs.get_datastore()

        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

        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

        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

        @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")
        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()
        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,
                                     {"presence": ONLINE})

        # Apple sees self-reflection
        (events, _) = yield self.event_source.get_new_events_for_user(
            self.u_apple, 0, None)

        self.assertEquals(self.event_source.get_current_key(), 1)
        self.assertEquals(
            events, [
                {
                    "type": "m.presence",
                    "content": {
                        "user_id": "@apple:test",
                        "presence": ONLINE,
                        "last_active_ago": 0,
                    }
                },
            ],
            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,
                        "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,
                        "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)

        self.assertEquals([
            {
                "observed_user": self.u_banana,
                "presence": OFFLINE
            },
            {
                "observed_user": self.u_clementine,
                "presence": OFFLINE
            },
        ], presence)

        # 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,
                                     {"presence": ONLINE})

        self.clock.advance_time(2)

        presence = yield self.handler.get_presence_list(
            observer_user=self.u_apple, accepted=True)

        self.assertEquals([
            {
                "observed_user": self.u_banana,
                "presence": ONLINE,
                "last_active_ago": 2000
            },
            {
                "observed_user": self.u_clementine,
                "presence": OFFLINE
            },
        ], presence)

        (events, _) = yield self.event_source.get_new_events_for_user(
            self.u_apple, 1, None)

        self.assertEquals(self.event_source.get_current_key(), 2)
        self.assertEquals(events, [
            {
                "type": "m.presence",
                "content": {
                    "user_id": "@banana:test",
                    "presence": ONLINE,
                    "last_active_ago": 2000
                }
            },
        ])

    @defer.inlineCallbacks
    def test_push_remote(self):
        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",
                                             "presence": u"online",
                                             "last_active_ago": 0
                                         },
                                     ],
                                 }),
                on_send_callback=ANY,
            ),
            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": "@apple:test",
                                             "presence": u"online",
                                             "last_active_ago": 0
                                         },
                                     ],
                                 }),
                on_send_callback=ANY,
            ),
            defer.succeed((200, "OK")))

        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()
        self.handler._user_cachemap[self.u_apple].update({"presence": OFFLINE},
                                                         serial=0)
        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,
                                     {"presence": ONLINE})

        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)

        yield self.mock_federation_resource.trigger(
            "PUT", "/_matrix/federation/v1/send/1000000/",
            _make_edu_json("elsewhere",
                           "m.presence",
                           content={
                               "push": [
                                   {
                                       "user_id": "@potato:remote",
                                       "presence": "online",
                                       "last_active_ago": 1000
                                   },
                               ],
                           }))

        (events, _) = yield self.event_source.get_new_events_for_user(
            self.u_apple, 0, None)

        self.assertEquals(self.event_source.get_current_key(), 1)
        self.assertEquals(events, [{
            "type": "m.presence",
            "content": {
                "user_id": "@potato:remote",
                "presence": ONLINE,
                "last_active_ago": 1000,
            }
        }])

        self.clock.advance_time(2)

        state = yield self.handler.get_state(self.u_potato, self.u_apple)

        self.assertEquals({"presence": ONLINE, "last_active_ago": 3000}, 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(
            {
                "presence": PresenceState.ONLINE,
                "last_active": self.clock.time_msec(),
            }, self.u_clementine)

        yield self.distributor.fire("user_joined_room", self.u_clementine,
                                    "a-room")

        self.room_members.append(self.u_clementine)

        (events, _) = yield self.event_source.get_new_events_for_user(
            self.u_apple, 0, None)

        self.assertEquals(self.event_source.get_current_key(), 1)
        self.assertEquals(events, [{
            "type": "m.presence",
            "content": {
                "user_id": "@clementine:test",
                "presence": ONLINE,
                "last_active_ago": 0,
            }
        }])

    @defer.inlineCallbacks
    def test_join_room_remote(self):
        ## Sending local user state to a newly-joined remote user
        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",
                                             "presence": "online"
                                         },
                                     ],
                                 }),
                on_send_callback=ANY,
            ),
            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",
                                             "presence": "offline"
                                         },
                                     ],
                                 }),
                on_send_callback=ANY,
            ),
            defer.succeed((200, "OK")))

        # TODO(paul): Gut-wrenching
        self.handler._user_cachemap[self.u_apple] = UserPresenceCache()
        self.handler._user_cachemap[self.u_apple].update(
            {"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")

        yield put_json.await_calls()

        ## Sending newly-joined local user state to remote users

        put_json.expect_call_and_return(
            call(
                "remote",
                path="/_matrix/federation/v1/send/1000002/",
                data=_expect_edu("remote",
                                 "m.presence",
                                 content={
                                     "push": [
                                         {
                                             "user_id": "@clementine:test",
                                             "presence": "online"
                                         },
                                     ],
                                 }),
                on_send_callback=ANY,
            ), defer.succeed((200, "OK")))

        self.handler._user_cachemap[self.u_clementine] = UserPresenceCache()
        self.handler._user_cachemap[self.u_clementine].update(
            {"presence": ONLINE}, self.u_clementine)
        self.room_members.append(self.u_potato)

        yield self.distributor.fire("user_joined_room", self.u_clementine,
                                    "a-room")

        put_json.await_calls()
Example #21
0
    def setUp(self):
        self.clock = MockClock()

        self.mock_http_client = Mock(spec=[])
        self.mock_http_client.put_json = DeferredMockCallable()

        self.mock_federation_resource = MockHttpResource()

        hs = HomeServer(
            "test",
            clock=self.clock,
            db_pool=None,
            datastore=Mock(spec=[
                "set_presence_state",
                "get_joined_hosts_for_room",

                # Bits that Federation needs
                "prep_send_transaction",
                "delivered_txn",
                "get_received_txn_response",
                "set_received_txn_response",
            ]),
            handlers=None,
            resource_for_client=Mock(),
            resource_for_federation=self.mock_federation_resource,
            http_client=self.mock_http_client,
        )
        hs.handlers = JustPresenceHandlers(hs)

        self.datastore = hs.get_datastore()

        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

        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

        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

        @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")
        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")
Example #22
0
    def setUp(self):
        self.clock = MockClock()
        self.cache = HttpTransactionCache(self.clock)

        self.mock_http_response = (200, "GOOD JOB!")
        self.mock_key = "foo"
Example #23
0
class HttpTransactionCacheTestCase(unittest.TestCase):
    def setUp(self):
        self.clock = MockClock()
        self.cache = HttpTransactionCache(self.clock)

        self.mock_http_response = (200, "GOOD JOB!")
        self.mock_key = "foo"

    @defer.inlineCallbacks
    def test_executes_given_function(self):
        cb = Mock(return_value=defer.succeed(self.mock_http_response))
        res = yield self.cache.fetch_or_execute(self.mock_key,
                                                cb,
                                                "some_arg",
                                                keyword="arg")
        cb.assert_called_once_with("some_arg", keyword="arg")
        self.assertEqual(res, self.mock_http_response)

    @defer.inlineCallbacks
    def test_deduplicates_based_on_key(self):
        cb = Mock(return_value=defer.succeed(self.mock_http_response))
        for i in range(3):  # invoke multiple times
            res = yield self.cache.fetch_or_execute(self.mock_key,
                                                    cb,
                                                    "some_arg",
                                                    keyword="arg",
                                                    changing_args=i)
            self.assertEqual(res, self.mock_http_response)
        # expect only a single call to do the work
        cb.assert_called_once_with("some_arg", keyword="arg", changing_args=0)

    @defer.inlineCallbacks
    def test_logcontexts_with_async_result(self):
        @defer.inlineCallbacks
        def cb():
            yield Clock(reactor).sleep(0)
            defer.returnValue("yay")

        @defer.inlineCallbacks
        def test():
            with LoggingContext("c") as c1:
                res = yield self.cache.fetch_or_execute(self.mock_key, cb)
                self.assertIs(LoggingContext.current_context(), c1)
                self.assertEqual(res, "yay")

        # run the test twice in parallel
        d = defer.gatherResults([test(), test()])
        self.assertIs(LoggingContext.current_context(),
                      LoggingContext.sentinel)
        yield d
        self.assertIs(LoggingContext.current_context(),
                      LoggingContext.sentinel)

    @defer.inlineCallbacks
    def test_does_not_cache_exceptions(self):
        """Checks that, if the callback throws an exception, it is called again
        for the next request.
        """
        called = [False]

        def cb():
            if called[0]:
                # return a valid result the second time
                return defer.succeed(self.mock_http_response)

            called[0] = True
            raise Exception("boo")

        with LoggingContext("test") as test_context:
            try:
                yield self.cache.fetch_or_execute(self.mock_key, cb)
            except Exception as e:
                self.assertEqual(e.message, "boo")
            self.assertIs(LoggingContext.current_context(), test_context)

            res = yield self.cache.fetch_or_execute(self.mock_key, cb)
            self.assertEqual(res, self.mock_http_response)
            self.assertIs(LoggingContext.current_context(), test_context)

    @defer.inlineCallbacks
    def test_does_not_cache_failures(self):
        """Checks that, if the callback returns a failure, it is called again
        for the next request.
        """
        called = [False]

        def cb():
            if called[0]:
                # return a valid result the second time
                return defer.succeed(self.mock_http_response)

            called[0] = True
            return defer.fail(Exception("boo"))

        with LoggingContext("test") as test_context:
            try:
                yield self.cache.fetch_or_execute(self.mock_key, cb)
            except Exception as e:
                self.assertEqual(e.message, "boo")
            self.assertIs(LoggingContext.current_context(), test_context)

            res = yield self.cache.fetch_or_execute(self.mock_key, cb)
            self.assertEqual(res, self.mock_http_response)
            self.assertIs(LoggingContext.current_context(), test_context)

    @defer.inlineCallbacks
    def test_cleans_up(self):
        cb = Mock(return_value=defer.succeed(self.mock_http_response))
        yield self.cache.fetch_or_execute(self.mock_key, cb, "an arg")
        # should NOT have cleaned up yet
        self.clock.advance_time_msec(CLEANUP_PERIOD_MS / 2)

        yield self.cache.fetch_or_execute(self.mock_key, cb, "an arg")
        # still using cache
        cb.assert_called_once_with("an arg")

        self.clock.advance_time_msec(CLEANUP_PERIOD_MS)

        yield self.cache.fetch_or_execute(self.mock_key, cb, "an arg")
        # no longer using cache
        self.assertEqual(cb.call_count, 2)
        self.assertEqual(cb.call_args_list,
                         [call("an arg", ), call("an arg", )])
Example #24
0
    def setUp(self):
        db_pool = SQLiteMemoryDbPool()
        yield db_pool.prepare()

        hs = HomeServer(
            "test",
            clock=MockClock(),
            db_pool=db_pool,
            handlers=None,
            resource_for_federation=Mock(),
            http_client=None,
        )
        hs.handlers = JustPresenceHandlers(hs)

        self.store = hs.get_datastore()

        # Mock the RoomMemberHandler
        room_member_handler = Mock(spec=[])
        hs.handlers.room_member_handler = 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")

        yield self.store.create_presence(self.u_apple.localpart)
        yield self.store.set_presence_state(self.u_apple.localpart, {
            "state": ONLINE,
            "status_msg": "Online"
        })

        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

        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.store.user_rooms_intersect = user_rooms_intersect

        self.mock_start = Mock()
        self.mock_stop = Mock()

        self.handler.start_polling_presence = self.mock_start
        self.handler.stop_polling_presence = self.mock_stop
Example #25
0
    def setUp(self):
        self.mock_http_client = Mock(spec=[])
        self.mock_http_client.put_json = DeferredMockCallable()

        self.mock_federation_resource = MockHttpResource()

        hs = HomeServer(
            "test",
            clock=MockClock(),
            db_pool=None,
            datastore=Mock(spec=[
                # Bits that Federation needs
                "prep_send_transaction",
                "delivered_txn",
                "get_received_txn_response",
                "set_received_txn_response",
            ]),
            handlers=None,
            resource_for_client=Mock(),
            resource_for_federation=self.mock_federation_resource,
            http_client=self.mock_http_client,
        )
        hs.handlers = JustPresenceHandlers(hs)

        self.datastore = hs.get_datastore()

        def get_received_txn_response(*args):
            return defer.succeed(None)

        self.datastore.get_received_txn_response = get_received_txn_response

        self.mock_update_client = Mock()

        def update(*args, **kwargs):
            # print "mock_update_client: Args=%s, kwargs=%s" %(args, kwargs,)
            return defer.succeed(None)

        self.mock_update_client.side_effect = update

        self.handler = hs.get_handlers().presence_handler
        self.handler.push_update_to_clients = self.mock_update_client

        hs.handlers.room_member_handler = Mock(spec=[
            "get_rooms_for_user",
        ])

        # For this test no users are ever in rooms
        def get_rooms_for_user(user):
            return defer.succeed([])

        hs.handlers.room_member_handler.get_rooms_for_user = get_rooms_for_user

        # Mocked database state
        # Local users always start offline
        self.current_user_state = {
            "apple": OFFLINE,
            "banana": OFFLINE,
            "clementine": OFFLINE,
            "fig": OFFLINE,
        }

        def get_presence_state(user_localpart):
            return defer.succeed({
                "state":
                self.current_user_state[user_localpart],
                "status_msg":
                None,
                "mtime":
                123456000
            })

        self.datastore.get_presence_state = get_presence_state

        def set_presence_state(user_localpart, new_state):
            was = self.current_user_state[user_localpart]
            self.current_user_state[user_localpart] = new_state["state"]
            return defer.succeed({"state": was})

        self.datastore.set_presence_state = set_presence_state

        def get_presence_list(user_localpart, accepted):
            return defer.succeed([{
                "observed_user_id": u
            } for u in self.PRESENCE_LIST[user_localpart]])

        self.datastore.get_presence_list = get_presence_list

        def is_presence_visible(observed_localpart, observer_userid):
            return True

        self.datastore.is_presence_visible = is_presence_visible

        # Local users
        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.u_fig = hs.parse_userid("@fig:test")

        # Remote users
        self.u_potato = hs.parse_userid("@potato:remote")
Example #26
0
def setup_test_homeserver(
    cleanup_func,
    name="test",
    config=None,
    reactor=None,
    homeserver_to_use: Type[HomeServer] = TestHomeServer,
    **kwargs,
):
    """
    Setup a homeserver suitable for running tests against.  Keyword arguments
    are passed to the Homeserver constructor.

    If no datastore is supplied, one is created and given to the homeserver.

    Args:
        cleanup_func : The function used to register a cleanup routine for
                       after the test.

    Calling this method directly is deprecated: you should instead derive from
    HomeserverTestCase.
    """
    if reactor is None:
        from twisted.internet import reactor

    if config is None:
        config = default_config(name, parse=True)

    config.ldap_enabled = False

    if "clock" not in kwargs:
        kwargs["clock"] = MockClock()

    if USE_POSTGRES_FOR_TESTS:
        test_db = "synapse_test_%s" % uuid.uuid4().hex

        database_config = {
            "name": "psycopg2",
            "args": {
                "database": test_db,
                "host": POSTGRES_HOST,
                "password": POSTGRES_PASSWORD,
                "user": POSTGRES_USER,
                "cp_min": 1,
                "cp_max": 5,
            },
        }
    else:
        if SQLITE_PERSIST_DB:
            # The current working directory is in _trial_temp, so this gets created within that directory.
            test_db_location = os.path.abspath("test.db")
            logger.debug("Will persist db to %s", test_db_location)
            # Ensure each test gets a clean database.
            try:
                os.remove(test_db_location)
            except FileNotFoundError:
                pass
            else:
                logger.debug("Removed existing DB at %s", test_db_location)
        else:
            test_db_location = ":memory:"

        database_config = {
            "name": "sqlite3",
            "args": {
                "database": test_db_location,
                "cp_min": 1,
                "cp_max": 1
            },
        }

    if "db_txn_limit" in kwargs:
        database_config["txn_limit"] = kwargs["db_txn_limit"]

    database = DatabaseConnectionConfig("master", database_config)
    config.database.databases = [database]

    db_engine = create_engine(database.config)

    # Create the database before we actually try and connect to it, based off
    # the template database we generate in setupdb()
    if isinstance(db_engine, PostgresEngine):
        db_conn = db_engine.module.connect(
            database=POSTGRES_BASE_DB,
            user=POSTGRES_USER,
            host=POSTGRES_HOST,
            password=POSTGRES_PASSWORD,
        )
        db_conn.autocommit = True
        cur = db_conn.cursor()
        cur.execute("DROP DATABASE IF EXISTS %s;" % (test_db, ))
        cur.execute("CREATE DATABASE %s WITH TEMPLATE %s;" %
                    (test_db, POSTGRES_BASE_DB))
        cur.close()
        db_conn.close()

    hs = homeserver_to_use(
        name,
        config=config,
        version_string="Synapse/tests",
        reactor=reactor,
    )

    # Install @cache_in_self attributes
    for key, val in kwargs.items():
        setattr(hs, "_" + key, val)

    # Mock TLS
    hs.tls_server_context_factory = Mock()
    hs.tls_client_options_factory = Mock()

    hs.setup()
    if homeserver_to_use == TestHomeServer:
        hs.setup_background_tasks()

    if isinstance(db_engine, PostgresEngine):
        database = hs.get_datastores().databases[0]

        # We need to do cleanup on PostgreSQL
        def cleanup():
            import psycopg2

            # Close all the db pools
            database._db_pool.close()

            dropped = False

            # Drop the test database
            db_conn = db_engine.module.connect(
                database=POSTGRES_BASE_DB,
                user=POSTGRES_USER,
                host=POSTGRES_HOST,
                password=POSTGRES_PASSWORD,
            )
            db_conn.autocommit = True
            cur = db_conn.cursor()

            # Try a few times to drop the DB. Some things may hold on to the
            # database for a few more seconds due to flakiness, preventing
            # us from dropping it when the test is over. If we can't drop
            # it, warn and move on.
            for _ in range(5):
                try:
                    cur.execute("DROP DATABASE IF EXISTS %s;" % (test_db, ))
                    db_conn.commit()
                    dropped = True
                except psycopg2.OperationalError as e:
                    warnings.warn("Couldn't drop old db: " + str(e),
                                  category=UserWarning)
                    time.sleep(0.5)

            cur.close()
            db_conn.close()

            if not dropped:
                warnings.warn("Failed to drop old DB.", category=UserWarning)

        if not LEAVE_DB:
            # Register the cleanup hook
            cleanup_func(cleanup)

    # bcrypt is far too slow to be doing in unit tests
    # Need to let the HS build an auth handler and then mess with it
    # because AuthHandler's constructor requires the HS, so we can't make one
    # beforehand and pass it in to the HS's constructor (chicken / egg)
    async def hash(p):
        return hashlib.md5(p.encode("utf8")).hexdigest()

    hs.get_auth_handler().hash = hash

    async def validate_hash(p, h):
        return hashlib.md5(p.encode("utf8")).hexdigest() == h

    hs.get_auth_handler().validate_hash = validate_hash

    # Make the threadpool and database transactions synchronous for testing.
    _make_test_homeserver_synchronous(hs)

    return hs
Example #27
0
class MockClockTestCase(unittest.TestCase):
    def setUp(self):
        self.clock = MockClock()

    def test_advance_time(self):
        start_time = self.clock.time()

        self.clock.advance_time(20)

        self.assertEquals(20, self.clock.time() - start_time)

    def test_later(self):
        invoked = [0, 0]

        def _cb0():
            invoked[0] = 1

        self.clock.call_later(10, _cb0)

        def _cb1():
            invoked[1] = 1

        self.clock.call_later(20, _cb1)

        self.assertFalse(invoked[0])

        self.clock.advance_time(15)

        self.assertTrue(invoked[0])
        self.assertFalse(invoked[1])

        self.clock.advance_time(5)

        self.assertTrue(invoked[1])

    def test_cancel_later(self):
        invoked = [0, 0]

        def _cb0():
            invoked[0] = 1

        t0 = self.clock.call_later(10, _cb0)

        def _cb1():
            invoked[1] = 1

        self.clock.call_later(20, _cb1)

        self.clock.cancel_call_later(t0)

        self.clock.advance_time(30)

        self.assertFalse(invoked[0])
        self.assertTrue(invoked[1])
Example #28
0
class HttpTransactionCacheTestCase(unittest.TestCase):

    def setUp(self):
        self.clock = MockClock()
        self.cache = HttpTransactionCache(self.clock)

        self.mock_http_response = (200, "GOOD JOB!")
        self.mock_key = "foo"

    @defer.inlineCallbacks
    def test_executes_given_function(self):
        cb = Mock(
            return_value=defer.succeed(self.mock_http_response)
        )
        res = yield self.cache.fetch_or_execute(
            self.mock_key, cb, "some_arg", keyword="arg"
        )
        cb.assert_called_once_with("some_arg", keyword="arg")
        self.assertEqual(res, self.mock_http_response)

    @defer.inlineCallbacks
    def test_deduplicates_based_on_key(self):
        cb = Mock(
            return_value=defer.succeed(self.mock_http_response)
        )
        for i in range(3):  # invoke multiple times
            res = yield self.cache.fetch_or_execute(
                self.mock_key, cb, "some_arg", keyword="arg", changing_args=i
            )
            self.assertEqual(res, self.mock_http_response)
        # expect only a single call to do the work
        cb.assert_called_once_with("some_arg", keyword="arg", changing_args=0)

    @defer.inlineCallbacks
    def test_cleans_up(self):
        cb = Mock(
            return_value=defer.succeed(self.mock_http_response)
        )
        yield self.cache.fetch_or_execute(
            self.mock_key, cb, "an arg"
        )
        # should NOT have cleaned up yet
        self.clock.advance_time_msec(CLEANUP_PERIOD_MS / 2)

        yield self.cache.fetch_or_execute(
            self.mock_key, cb, "an arg"
        )
        # still using cache
        cb.assert_called_once_with("an arg")

        self.clock.advance_time_msec(CLEANUP_PERIOD_MS)

        yield self.cache.fetch_or_execute(
            self.mock_key, cb, "an arg"
        )
        # no longer using cache
        self.assertEqual(cb.call_count, 2)
        self.assertEqual(
            cb.call_args_list,
            [call("an arg",), call("an arg",)]
        )
Example #29
0
    def setUp(self):
        self.clock = MockClock()
        self.cache = HttpTransactionCache(self.clock)

        self.mock_http_response = (200, "GOOD JOB!")
        self.mock_key = "foo"
Example #30
0
    def setUp(self):
        self.clock = MockClock()

        self.mock_http_client = Mock(spec=[])
        self.mock_http_client.put_json = DeferredMockCallable()

        self.mock_federation_resource = MockHttpResource()

        self.mock_config = NonCallableMock()
        self.mock_config.signing_key = [MockKey()]

        hs = HomeServer("test",
                clock=self.clock,
                db_pool=None,
                datastore=Mock(spec=[
                    "set_presence_state",
                    "get_joined_hosts_for_room",

                    # Bits that Federation needs
                    "prep_send_transaction",
                    "delivered_txn",
                    "get_received_txn_response",
                    "set_received_txn_response",
                ]),
                handlers=None,
                resource_for_client=Mock(),
                resource_for_federation=self.mock_federation_resource,
                http_client=self.mock_http_client,
                config=self.mock_config,
                keyring=Mock(),
            )
        hs.handlers = JustPresenceHandlers(hs)

        self.datastore = hs.get_datastore()

        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

        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

        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

        @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")
        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")
Example #31
0
class HttpTransactionCacheTestCase(unittest.TestCase):
    def setUp(self):
        self.clock = MockClock()
        self.hs = Mock()
        self.hs.get_clock = Mock(return_value=self.clock)
        self.hs.get_auth = Mock()
        self.cache = HttpTransactionCache(self.hs)

        self.mock_http_response = (200, "GOOD JOB!")
        self.mock_key = "foo"

    @defer.inlineCallbacks
    def test_executes_given_function(self):
        cb = Mock(return_value=defer.succeed(self.mock_http_response))
        res = yield self.cache.fetch_or_execute(
            self.mock_key, cb, "some_arg", keyword="arg"
        )
        cb.assert_called_once_with("some_arg", keyword="arg")
        self.assertEqual(res, self.mock_http_response)

    @defer.inlineCallbacks
    def test_deduplicates_based_on_key(self):
        cb = Mock(return_value=defer.succeed(self.mock_http_response))
        for i in range(3):  # invoke multiple times
            res = yield self.cache.fetch_or_execute(
                self.mock_key, cb, "some_arg", keyword="arg", changing_args=i
            )
            self.assertEqual(res, self.mock_http_response)
        # expect only a single call to do the work
        cb.assert_called_once_with("some_arg", keyword="arg", changing_args=0)

    @defer.inlineCallbacks
    def test_logcontexts_with_async_result(self):
        @defer.inlineCallbacks
        def cb():
            yield Clock(reactor).sleep(0)
            defer.returnValue("yay")

        @defer.inlineCallbacks
        def test():
            with LoggingContext("c") as c1:
                res = yield self.cache.fetch_or_execute(self.mock_key, cb)
                self.assertIs(LoggingContext.current_context(), c1)
                self.assertEqual(res, "yay")

        # run the test twice in parallel
        d = defer.gatherResults([test(), test()])
        self.assertIs(LoggingContext.current_context(), LoggingContext.sentinel)
        yield d
        self.assertIs(LoggingContext.current_context(), LoggingContext.sentinel)

    @defer.inlineCallbacks
    def test_does_not_cache_exceptions(self):
        """Checks that, if the callback throws an exception, it is called again
        for the next request.
        """
        called = [False]

        def cb():
            if called[0]:
                # return a valid result the second time
                return defer.succeed(self.mock_http_response)

            called[0] = True
            raise Exception("boo")

        with LoggingContext("test") as test_context:
            try:
                yield self.cache.fetch_or_execute(self.mock_key, cb)
            except Exception as e:
                self.assertEqual(e.args[0], "boo")
            self.assertIs(LoggingContext.current_context(), test_context)

            res = yield self.cache.fetch_or_execute(self.mock_key, cb)
            self.assertEqual(res, self.mock_http_response)
            self.assertIs(LoggingContext.current_context(), test_context)

    @defer.inlineCallbacks
    def test_does_not_cache_failures(self):
        """Checks that, if the callback returns a failure, it is called again
        for the next request.
        """
        called = [False]

        def cb():
            if called[0]:
                # return a valid result the second time
                return defer.succeed(self.mock_http_response)

            called[0] = True
            return defer.fail(Exception("boo"))

        with LoggingContext("test") as test_context:
            try:
                yield self.cache.fetch_or_execute(self.mock_key, cb)
            except Exception as e:
                self.assertEqual(e.args[0], "boo")
            self.assertIs(LoggingContext.current_context(), test_context)

            res = yield self.cache.fetch_or_execute(self.mock_key, cb)
            self.assertEqual(res, self.mock_http_response)
            self.assertIs(LoggingContext.current_context(), test_context)

    @defer.inlineCallbacks
    def test_cleans_up(self):
        cb = Mock(return_value=defer.succeed(self.mock_http_response))
        yield self.cache.fetch_or_execute(self.mock_key, cb, "an arg")
        # should NOT have cleaned up yet
        self.clock.advance_time_msec(CLEANUP_PERIOD_MS / 2)

        yield self.cache.fetch_or_execute(self.mock_key, cb, "an arg")
        # still using cache
        cb.assert_called_once_with("an arg")

        self.clock.advance_time_msec(CLEANUP_PERIOD_MS)

        yield self.cache.fetch_or_execute(self.mock_key, cb, "an arg")
        # no longer using cache
        self.assertEqual(cb.call_count, 2)
        self.assertEqual(cb.call_args_list, [call("an arg"), call("an arg")])
Example #32
0
 def setUp(self):
     self.clock = MockClock()
Example #33
0
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):
        self.clock = MockClock()

        self.mock_http_client = Mock(spec=[])
        self.mock_http_client.put_json = DeferredMockCallable()

        self.mock_federation_resource = MockHttpResource()

        self.mock_config = NonCallableMock()
        self.mock_config.signing_key = [MockKey()]

        hs = HomeServer("test",
                clock=self.clock,
                db_pool=None,
                datastore=Mock(spec=[
                    "set_presence_state",
                    "get_joined_hosts_for_room",

                    # Bits that Federation needs
                    "prep_send_transaction",
                    "delivered_txn",
                    "get_received_txn_response",
                    "set_received_txn_response",
                ]),
                handlers=None,
                resource_for_client=Mock(),
                resource_for_federation=self.mock_federation_resource,
                http_client=self.mock_http_client,
                config=self.mock_config,
                keyring=Mock(),
            )
        hs.handlers = JustPresenceHandlers(hs)

        self.datastore = hs.get_datastore()

        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

        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

        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

        @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")
        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()
        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,
            {"presence": ONLINE}
        )

        # Apple sees self-reflection
        (events, _) = yield self.event_source.get_new_events_for_user(
            self.u_apple, 0, None
        )

        self.assertEquals(self.event_source.get_current_key(), 1)
        self.assertEquals(events,
            [
                {"type": "m.presence",
                 "content": {
                    "user_id": "@apple:test",
                    "presence": ONLINE,
                    "last_active_ago": 0,
                }},
            ],
            msg="Presence event should be visible to self-reflection"
        )

        config = SourcePaginationConfig(from_key=1, to_key=0)
        (chunk, _) = yield self.event_source.get_pagination_rows(
            self.u_apple, config, None
        )
        self.assertEquals(chunk,
            [
                {"type": "m.presence",
                 "content": {
                     "user_id": "@apple:test",
                     "presence": ONLINE,
                     "last_active_ago": 0,
                }},
            ]
        )

        # 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,
                    "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,
                    "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)

        self.assertEquals(
            [
                {"observed_user": self.u_banana, 
                 "presence": OFFLINE},
                {"observed_user": self.u_clementine,
                 "presence": OFFLINE},
            ],
            presence
        )

        # 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,
            {"presence": ONLINE}
        )

        self.clock.advance_time(2)

        presence = yield self.handler.get_presence_list(
                observer_user=self.u_apple, accepted=True)

        self.assertEquals([
                {"observed_user": self.u_banana,
                 "presence": ONLINE,
                 "last_active_ago": 2000},
                {"observed_user": self.u_clementine,
                 "presence": OFFLINE},
        ], presence)

        (events, _) = yield self.event_source.get_new_events_for_user(
            self.u_apple, 1, None
        )

        self.assertEquals(self.event_source.get_current_key(), 2)
        self.assertEquals(events,
            [
                {"type": "m.presence",
                 "content": {
                     "user_id": "@banana:test",
                     "presence": ONLINE,
                     "last_active_ago": 2000
                }},
            ]
        )

    @defer.inlineCallbacks
    def test_push_remote(self):
        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",
                             "presence": u"online",
                             "last_active_ago": 0},
                        ],
                    }
                ),
                json_data_callback=ANY,
            ),
            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": "@apple:test",
                             "presence": u"online",
                             "last_active_ago": 0},
                        ],
                    }
                ),
                json_data_callback=ANY,
            ),
            defer.succeed((200, "OK"))
        )

        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()
        self.handler._user_cachemap[self.u_apple].update(
            {"presence": OFFLINE}, serial=0
        )
        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,
            {"presence": ONLINE}
        )

        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)

        yield self.mock_federation_resource.trigger("PUT",
            "/_matrix/federation/v1/send/1000000/",
            _make_edu_json("elsewhere", "m.presence",
                content={
                    "push": [
                        {"user_id": "@potato:remote",
                         "presence": "online",
                         "last_active_ago": 1000},
                    ],
                }
            )
        )

        (events, _) = yield self.event_source.get_new_events_for_user(
            self.u_apple, 0, None
        )

        self.assertEquals(self.event_source.get_current_key(), 1)
        self.assertEquals(events,
            [
                {"type": "m.presence",
                 "content": {
                     "user_id": "@potato:remote",
                     "presence": ONLINE,
                     "last_active_ago": 1000,
                }}
            ]
        )

        self.clock.advance_time(2)

        state = yield self.handler.get_state(self.u_potato, self.u_apple)

        self.assertEquals(
            {"presence": ONLINE, "last_active_ago": 3000},
            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(
            {
                "presence": PresenceState.ONLINE,
                "last_active": self.clock.time_msec(),
            }, self.u_clementine
        )

        yield self.distributor.fire("user_joined_room", self.u_clementine,
            "a-room"
        )

        self.room_members.append(self.u_clementine)

        (events, _) = yield self.event_source.get_new_events_for_user(
            self.u_apple, 0, None
        )

        self.assertEquals(self.event_source.get_current_key(), 1)
        self.assertEquals(events,
            [
                {"type": "m.presence",
                 "content": {
                     "user_id": "@clementine:test",
                     "presence": ONLINE,
                     "last_active_ago": 0,
                }}
            ]
        )

    @defer.inlineCallbacks
    def test_join_room_remote(self):
        ## Sending local user state to a newly-joined remote user
        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",
                             "presence": "online"},
                        ],
                    }
                ),
                json_data_callback=ANY,
            ),
            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",
                             "presence": "offline"},
                        ],
                    }
                ),
                json_data_callback=ANY,
            ),
            defer.succeed((200, "OK"))
        )

        # TODO(paul): Gut-wrenching
        self.handler._user_cachemap[self.u_apple] = UserPresenceCache()
        self.handler._user_cachemap[self.u_apple].update(
                {"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"
        )

        yield put_json.await_calls()

        ## Sending newly-joined local user state to remote users

        put_json.expect_call_and_return(
            call("remote",
                path="/_matrix/federation/v1/send/1000002/",
                data=_expect_edu("remote", "m.presence",
                    content={
                        "push": [
                            {"user_id": "@clementine:test",
                             "presence": "online"},
                        ],
                    }
                ),
                json_data_callback=ANY,
            ),
            defer.succeed((200, "OK"))
        )

        self.handler._user_cachemap[self.u_clementine] = UserPresenceCache()
        self.handler._user_cachemap[self.u_clementine].update(
                {"presence": ONLINE}, self.u_clementine)
        self.room_members.append(self.u_potato)

        yield self.distributor.fire("user_joined_room", self.u_clementine,
            "a-room"
        )

        put_json.await_calls()