summary refs log tree commit diff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/appservice/test_appservice.py1
-rw-r--r--tests/handlers/test_federation.py141
-rw-r--r--tests/handlers/test_room.py418
-rw-r--r--tests/storage/test_appservice.py101
4 files changed, 86 insertions, 575 deletions
diff --git a/tests/appservice/test_appservice.py b/tests/appservice/test_appservice.py
index 191c420c4d..ef48bbc296 100644
--- a/tests/appservice/test_appservice.py
+++ b/tests/appservice/test_appservice.py
@@ -29,6 +29,7 @@ class ApplicationServiceTestCase(unittest.TestCase):
 
     def setUp(self):
         self.service = ApplicationService(
+            id="unique_identifier",
             url="some_url",
             token="some_token",
             namespaces={
diff --git a/tests/handlers/test_federation.py b/tests/handlers/test_federation.py
deleted file mode 100644
index 11a3d94bb0..0000000000
--- a/tests/handlers/test_federation.py
+++ /dev/null
@@ -1,141 +0,0 @@
-# Copyright 2014-2016 OpenMarket Ltd
-#
-# 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.
-
-
-from twisted.internet import defer
-from tests import unittest
-
-from synapse.api.constants import EventTypes
-from synapse.events import FrozenEvent
-from synapse.handlers.federation import FederationHandler
-
-from mock import NonCallableMock, ANY, Mock
-
-from ..utils import setup_test_homeserver
-
-
-class FederationTestCase(unittest.TestCase):
-
-    @defer.inlineCallbacks
-    def setUp(self):
-
-        self.state_handler = NonCallableMock(spec_set=[
-            "compute_event_context",
-        ])
-
-        self.auth = NonCallableMock(spec_set=[
-            "check",
-            "check_host_in_room",
-        ])
-
-        self.hostname = "test"
-        hs = yield setup_test_homeserver(
-            self.hostname,
-            datastore=NonCallableMock(spec_set=[
-                "persist_event",
-                "store_room",
-                "get_room",
-                "get_destination_retry_timings",
-                "set_destination_retry_timings",
-                "have_events",
-                "get_users_in_room",
-                "bulk_get_push_rules",
-                "get_current_state",
-                "set_push_actions_for_event_and_users",
-                "is_guest",
-                "get_state_for_events",
-            ]),
-            resource_for_federation=NonCallableMock(),
-            http_client=NonCallableMock(spec_set=[]),
-            notifier=NonCallableMock(spec_set=["on_new_room_event"]),
-            handlers=NonCallableMock(spec_set=[
-                "room_member_handler",
-                "federation_handler",
-            ]),
-            auth=self.auth,
-            state_handler=self.state_handler,
-            keyring=Mock(),
-        )
-
-        self.datastore = hs.get_datastore()
-        self.handlers = hs.get_handlers()
-        self.notifier = hs.get_notifier()
-        self.hs = hs
-
-        self.handlers.federation_handler = FederationHandler(self.hs)
-
-        self.datastore.get_state_for_events.return_value = {"$a:b": {}}
-
-    @defer.inlineCallbacks
-    def test_msg(self):
-        pdu = FrozenEvent({
-            "type": EventTypes.Message,
-            "room_id": "foo",
-            "content": {"msgtype": u"fooo"},
-            "origin_server_ts": 0,
-            "event_id": "$a:b",
-            "user_id":"@a:b",
-            "origin": "b",
-            "auth_events": [],
-            "hashes": {"sha256":"AcLrgtUIqqwaGoHhrEvYG1YLDIsVPYJdSRGhkp3jJp8"},
-        })
-
-        self.datastore.persist_event.return_value = defer.succeed((1,1))
-        self.datastore.get_room.return_value = defer.succeed(True)
-        self.datastore.get_users_in_room.return_value = ["@a:b"]
-        self.datastore.bulk_get_push_rules.return_value = {}
-        self.datastore.get_current_state.return_value = {}
-        self.auth.check_host_in_room.return_value = defer.succeed(True)
-
-        retry_timings_res = {
-            "destination": "",
-            "retry_last_ts": 0,
-            "retry_interval": 0,
-        }
-        self.datastore.get_destination_retry_timings.return_value = (
-            defer.succeed(retry_timings_res)
-        )
-
-        def have_events(event_ids):
-            return defer.succeed({})
-        self.datastore.have_events.side_effect = have_events
-
-        def annotate(ev, old_state=None, outlier=False):
-            context = Mock()
-            context.current_state = {}
-            context.auth_events = {}
-            return defer.succeed(context)
-        self.state_handler.compute_event_context.side_effect = annotate
-
-        yield self.handlers.federation_handler.on_receive_pdu(
-            "fo", pdu, False
-        )
-
-        self.datastore.persist_event.assert_called_once_with(
-            ANY,
-            is_new_state=True,
-            backfilled=False,
-            current_state=None,
-            context=ANY,
-        )
-
-        self.state_handler.compute_event_context.assert_called_once_with(
-            ANY, old_state=None, outlier=False
-        )
-
-        self.auth.check.assert_called_once_with(ANY, auth_events={})
-
-        self.notifier.on_new_room_event.assert_called_once_with(
-            ANY, 1, 1, extra_users=[]
-        )
diff --git a/tests/handlers/test_room.py b/tests/handlers/test_room.py
deleted file mode 100644
index 97491848a3..0000000000
--- a/tests/handlers/test_room.py
+++ /dev/null
@@ -1,418 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright 2014-2016 OpenMarket Ltd
-#
-# 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.
-
-
-from twisted.internet import defer
-from .. import unittest
-
-from synapse.api.constants import EventTypes, Membership
-from synapse.handlers.room import RoomMemberHandler, RoomCreationHandler
-from synapse.handlers.profile import ProfileHandler
-from synapse.types import UserID
-from ..utils import setup_test_homeserver
-
-from mock import Mock, NonCallableMock
-
-
-class RoomMemberHandlerTestCase(unittest.TestCase):
-
-    @defer.inlineCallbacks
-    def setUp(self):
-        self.hostname = "red"
-        hs = yield setup_test_homeserver(
-            self.hostname,
-            ratelimiter=NonCallableMock(spec_set=[
-                "send_message",
-            ]),
-            datastore=NonCallableMock(spec_set=[
-                "persist_event",
-                "get_room_member",
-                "get_room",
-                "store_room",
-                "get_latest_events_in_room",
-                "add_event_hashes",
-                "get_users_in_room",
-                "bulk_get_push_rules",
-                "get_current_state",
-                "set_push_actions_for_event_and_users",
-                "get_state_for_events",
-                "is_guest",
-            ]),
-            resource_for_federation=NonCallableMock(),
-            http_client=NonCallableMock(spec_set=[]),
-            notifier=NonCallableMock(spec_set=["on_new_room_event"]),
-            handlers=NonCallableMock(spec_set=[
-                "room_member_handler",
-                "profile_handler",
-                "federation_handler",
-            ]),
-            auth=NonCallableMock(spec_set=[
-                "check",
-                "add_auth_events",
-                "check_host_in_room",
-            ]),
-            state_handler=NonCallableMock(spec_set=[
-                "compute_event_context",
-                "get_current_state",
-            ]),
-        )
-
-        self.federation = NonCallableMock(spec_set=[
-            "handle_new_event",
-            "send_invite",
-            "get_state_for_room",
-        ])
-
-        self.datastore = hs.get_datastore()
-        self.handlers = hs.get_handlers()
-        self.notifier = hs.get_notifier()
-        self.state_handler = hs.get_state_handler()
-        self.distributor = hs.get_distributor()
-        self.auth = hs.get_auth()
-        self.hs = hs
-
-        self.handlers.federation_handler = self.federation
-
-        self.distributor.declare("collect_presencelike_data")
-
-        self.handlers.room_member_handler = RoomMemberHandler(self.hs)
-        self.handlers.profile_handler = ProfileHandler(self.hs)
-        self.room_member_handler = self.handlers.room_member_handler
-
-        self.ratelimiter = hs.get_ratelimiter()
-        self.ratelimiter.send_message.return_value = (True, 0)
-
-        self.datastore.persist_event.return_value = (1,1)
-        self.datastore.add_event_hashes.return_value = []
-        self.datastore.get_users_in_room.return_value = ["@bob:red"]
-        self.datastore.bulk_get_push_rules.return_value = {}
-
-    @defer.inlineCallbacks
-    def test_invite(self):
-        room_id = "!foo:red"
-        user_id = "@bob:red"
-        target_user_id = "@red:blue"
-        content = {"membership": Membership.INVITE}
-
-        builder = self.hs.get_event_builder_factory().new({
-            "type": EventTypes.Member,
-            "sender": user_id,
-            "state_key": target_user_id,
-            "room_id": room_id,
-            "content": content,
-        })
-
-        self.datastore.get_latest_events_in_room.return_value = (
-            defer.succeed([])
-        )
-        self.datastore.get_current_state.return_value = {}
-        self.datastore.get_state_for_events = lambda event_ids,types: {x: {} for x in event_ids}
-
-        def annotate(_):
-            ctx = Mock()
-            ctx.current_state = {
-                (EventTypes.Member, "@alice:green"): self._create_member(
-                    user_id="@alice:green",
-                    room_id=room_id,
-                ),
-                (EventTypes.Member, "@bob:red"): self._create_member(
-                    user_id="@bob:red",
-                    room_id=room_id,
-                ),
-            }
-            ctx.prev_state_events = []
-
-            return defer.succeed(ctx)
-
-        self.state_handler.compute_event_context.side_effect = annotate
-
-        def add_auth(_, ctx):
-            ctx.auth_events = ctx.current_state[
-                (EventTypes.Member, "@bob:red")
-            ]
-
-            return defer.succeed(True)
-        self.auth.add_auth_events.side_effect = add_auth
-
-        def send_invite(domain, event):
-            return defer.succeed(event)
-
-        self.federation.send_invite.side_effect = send_invite
-
-        room_handler = self.room_member_handler
-        event, context = yield room_handler._create_new_client_event(
-            builder
-        )
-
-        yield room_handler.change_membership(event, context)
-
-        self.state_handler.compute_event_context.assert_called_once_with(
-            builder
-        )
-
-        self.auth.add_auth_events.assert_called_once_with(
-            builder, context
-        )
-
-        self.federation.send_invite.assert_called_once_with(
-            "blue", event,
-        )
-
-        self.datastore.persist_event.assert_called_once_with(
-            event, context=context,
-        )
-        self.notifier.on_new_room_event.assert_called_once_with(
-            event, 1, 1, extra_users=[UserID.from_string(target_user_id)]
-        )
-        self.assertFalse(self.datastore.get_room.called)
-        self.assertFalse(self.datastore.store_room.called)
-        self.assertFalse(self.federation.get_state_for_room.called)
-
-    @defer.inlineCallbacks
-    def test_simple_join(self):
-        room_id = "!foo:red"
-        user_id = "@bob:red"
-        user = UserID.from_string(user_id)
-
-        join_signal_observer = Mock()
-        self.distributor.observe("user_joined_room", join_signal_observer)
-
-        builder = self.hs.get_event_builder_factory().new({
-            "type": EventTypes.Member,
-            "sender": user_id,
-            "state_key": user_id,
-            "room_id": room_id,
-            "content": {"membership": Membership.JOIN},
-        })
-
-        self.datastore.get_latest_events_in_room.return_value = (
-            defer.succeed([])
-        )
-        self.datastore.get_current_state.return_value = {}
-        self.datastore.get_state_for_events = lambda event_ids,types: {x: {} for x in event_ids}
-
-        def annotate(_):
-            ctx = Mock()
-            ctx.current_state = {
-                (EventTypes.Member, "@bob:red"): self._create_member(
-                    user_id="@bob:red",
-                    room_id=room_id,
-                    membership=Membership.INVITE
-                ),
-            }
-            ctx.prev_state_events = []
-
-            return defer.succeed(ctx)
-
-        self.state_handler.compute_event_context.side_effect = annotate
-
-        def add_auth(_, ctx):
-            ctx.auth_events = ctx.current_state[
-                (EventTypes.Member, "@bob:red")
-            ]
-
-            return defer.succeed(True)
-        self.auth.add_auth_events.side_effect = add_auth
-
-        room_handler = self.room_member_handler
-        event, context = yield room_handler._create_new_client_event(
-            builder
-        )
-
-        # Actual invocation
-        yield room_handler.change_membership(event, context)
-
-        self.federation.handle_new_event.assert_called_once_with(
-            event, destinations=set()
-        )
-
-        self.datastore.persist_event.assert_called_once_with(
-            event, context=context
-        )
-        self.notifier.on_new_room_event.assert_called_once_with(
-            event, 1, 1, extra_users=[user]
-        )
-
-        join_signal_observer.assert_called_with(
-            user=user, room_id=room_id
-        )
-
-    def _create_member(self, user_id, room_id, membership=Membership.JOIN):
-        builder = self.hs.get_event_builder_factory().new({
-            "type": EventTypes.Member,
-            "sender": user_id,
-            "state_key": user_id,
-            "room_id": room_id,
-            "content": {"membership": membership},
-        })
-
-        return builder.build()
-
-    @defer.inlineCallbacks
-    def test_simple_leave(self):
-        room_id = "!foo:red"
-        user_id = "@bob:red"
-        user = UserID.from_string(user_id)
-
-        builder = self.hs.get_event_builder_factory().new({
-            "type": EventTypes.Member,
-            "sender": user_id,
-            "state_key": user_id,
-            "room_id": room_id,
-            "content": {"membership": Membership.LEAVE},
-        })
-
-        self.datastore.get_latest_events_in_room.return_value = (
-            defer.succeed([])
-        )
-        self.datastore.get_current_state.return_value = {}
-        self.datastore.get_state_for_events = lambda event_ids,types: {x: {} for x in event_ids}
-
-        def annotate(_):
-            ctx = Mock()
-            ctx.current_state = {
-                (EventTypes.Member, "@bob:red"): self._create_member(
-                    user_id="@bob:red",
-                    room_id=room_id,
-                    membership=Membership.JOIN
-                ),
-            }
-            ctx.prev_state_events = []
-
-            return defer.succeed(ctx)
-
-        self.state_handler.compute_event_context.side_effect = annotate
-
-        def add_auth(_, ctx):
-            ctx.auth_events = ctx.current_state[
-                (EventTypes.Member, "@bob:red")
-            ]
-
-            return defer.succeed(True)
-        self.auth.add_auth_events.side_effect = add_auth
-
-        room_handler = self.room_member_handler
-        event, context = yield room_handler._create_new_client_event(
-            builder
-        )
-
-        leave_signal_observer = Mock()
-        self.distributor.observe("user_left_room", leave_signal_observer)
-
-        # Actual invocation
-        yield room_handler.change_membership(event, context)
-
-        self.federation.handle_new_event.assert_called_once_with(
-            event, destinations=set(['red'])
-        )
-
-        self.datastore.persist_event.assert_called_once_with(
-            event, context=context
-        )
-        self.notifier.on_new_room_event.assert_called_once_with(
-            event, 1, 1, extra_users=[user]
-        )
-
-        leave_signal_observer.assert_called_with(
-            user=user, room_id=room_id
-        )
-
-
-class RoomCreationTest(unittest.TestCase):
-
-    @defer.inlineCallbacks
-    def setUp(self):
-        self.hostname = "red"
-
-        hs = yield setup_test_homeserver(
-            self.hostname,
-            datastore=NonCallableMock(spec_set=[
-                "store_room",
-                "snapshot_room",
-                "persist_event",
-                "get_joined_hosts_for_room",
-            ]),
-            http_client=NonCallableMock(spec_set=[]),
-            notifier=NonCallableMock(spec_set=["on_new_room_event"]),
-            handlers=NonCallableMock(spec_set=[
-                "room_creation_handler",
-                "message_handler",
-            ]),
-            auth=NonCallableMock(spec_set=["check", "add_auth_events"]),
-            ratelimiter=NonCallableMock(spec_set=[
-                "send_message",
-            ]),
-        )
-
-        self.federation = NonCallableMock(spec_set=[
-            "handle_new_event",
-        ])
-
-        self.handlers = hs.get_handlers()
-
-        self.handlers.room_creation_handler = RoomCreationHandler(hs)
-        self.room_creation_handler = self.handlers.room_creation_handler
-
-        self.message_handler = self.handlers.message_handler
-
-        self.ratelimiter = hs.get_ratelimiter()
-        self.ratelimiter.send_message.return_value = (True, 0)
-
-    @defer.inlineCallbacks
-    def test_room_creation(self):
-        user_id = "@foo:red"
-        room_id = "!bobs_room:red"
-        config = {"visibility": "private"}
-
-        yield self.room_creation_handler.create_room(
-            user_id=user_id,
-            room_id=room_id,
-            config=config,
-        )
-
-        self.assertTrue(self.message_handler.create_and_send_event.called)
-
-        event_dicts = [
-            e[0][0]
-            for e in self.message_handler.create_and_send_event.call_args_list
-        ]
-
-        self.assertTrue(len(event_dicts) > 3)
-
-        self.assertDictContainsSubset(
-            {
-                "type": EventTypes.Create,
-                "sender": user_id,
-                "room_id": room_id,
-            },
-            event_dicts[0]
-        )
-
-        self.assertEqual(user_id, event_dicts[0]["content"]["creator"])
-
-        self.assertDictContainsSubset(
-            {
-                "type": EventTypes.Member,
-                "sender": user_id,
-                "room_id": room_id,
-                "state_key": user_id,
-            },
-            event_dicts[1]
-        )
-
-        self.assertEqual(
-            Membership.JOIN,
-            event_dicts[1]["content"]["membership"]
-        )
diff --git a/tests/storage/test_appservice.py b/tests/storage/test_appservice.py
index a5a464640f..5abecdf6e0 100644
--- a/tests/storage/test_appservice.py
+++ b/tests/storage/test_appservice.py
@@ -12,12 +12,13 @@
 # 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.
+import tempfile
+from synapse.config._base import ConfigError
 from tests import unittest
 from twisted.internet import defer
 
 from tests.utils import setup_test_homeserver
 from synapse.appservice import ApplicationService, ApplicationServiceState
-from synapse.server import HomeServer
 from synapse.storage.appservice import (
     ApplicationServiceStore, ApplicationServiceTransactionStore
 )
@@ -26,7 +27,6 @@ import json
 import os
 import yaml
 from mock import Mock
-from tests.utils import SQLiteMemoryDbPool, MockClock
 
 
 class ApplicationServiceStoreTestCase(unittest.TestCase):
@@ -41,9 +41,16 @@ class ApplicationServiceStoreTestCase(unittest.TestCase):
 
         self.as_token = "token1"
         self.as_url = "some_url"
-        self._add_appservice(self.as_token, self.as_url, "some_hs_token", "bob")
-        self._add_appservice("token2", "some_url", "some_hs_token", "bob")
-        self._add_appservice("token3", "some_url", "some_hs_token", "bob")
+        self.as_id = "as1"
+        self._add_appservice(
+            self.as_token,
+            self.as_id,
+            self.as_url,
+            "some_hs_token",
+            "bob"
+        )
+        self._add_appservice("token2", "as2", "some_url", "some_hs_token", "bob")
+        self._add_appservice("token3", "as3", "some_url", "some_hs_token", "bob")
         # must be done after inserts
         self.store = ApplicationServiceStore(hs)
 
@@ -55,9 +62,9 @@ class ApplicationServiceStoreTestCase(unittest.TestCase):
             except:
                 pass
 
-    def _add_appservice(self, as_token, url, hs_token, sender):
+    def _add_appservice(self, as_token, id, url, hs_token, sender):
         as_yaml = dict(url=url, as_token=as_token, hs_token=hs_token,
-                       sender_localpart=sender, namespaces={})
+                       id=id, sender_localpart=sender, namespaces={})
         # use the token as the filename
         with open(as_token, 'w') as outfile:
             outfile.write(yaml.dump(as_yaml))
@@ -74,6 +81,7 @@ class ApplicationServiceStoreTestCase(unittest.TestCase):
             self.as_token
         )
         self.assertEquals(stored_service.token, self.as_token)
+        self.assertEquals(stored_service.id, self.as_id)
         self.assertEquals(stored_service.url, self.as_url)
         self.assertEquals(
             stored_service.namespaces[ApplicationService.NS_ALIASES],
@@ -110,34 +118,34 @@ class ApplicationServiceTransactionStoreTestCase(unittest.TestCase):
             {
                 "token": "token1",
                 "url": "https://matrix-as.org",
-                "id": "token1"
+                "id": "id_1"
             },
             {
                 "token": "alpha_tok",
                 "url": "https://alpha.com",
-                "id": "alpha_tok"
+                "id": "id_alpha"
             },
             {
                 "token": "beta_tok",
                 "url": "https://beta.com",
-                "id": "beta_tok"
+                "id": "id_beta"
             },
             {
-                "token": "delta_tok",
-                "url": "https://delta.com",
-                "id": "delta_tok"
+                "token": "gamma_tok",
+                "url": "https://gamma.com",
+                "id": "id_gamma"
             },
         ]
         for s in self.as_list:
-            yield self._add_service(s["url"], s["token"])
+            yield self._add_service(s["url"], s["token"], s["id"])
 
         self.as_yaml_files = []
 
         self.store = TestTransactionStore(hs)
 
-    def _add_service(self, url, as_token):
+    def _add_service(self, url, as_token, id):
         as_yaml = dict(url=url, as_token=as_token, hs_token="something",
-                       sender_localpart="a_sender", namespaces={})
+                       id=id, sender_localpart="a_sender", namespaces={})
         # use the token as the filename
         with open(as_token, 'w') as outfile:
             outfile.write(yaml.dump(as_yaml))
@@ -405,3 +413,64 @@ class TestTransactionStore(ApplicationServiceTransactionStore,
 
     def __init__(self, hs):
         super(TestTransactionStore, self).__init__(hs)
+
+
+class ApplicationServiceStoreConfigTestCase(unittest.TestCase):
+
+    def _write_config(self, suffix, **kwargs):
+        vals = {
+            "id": "id" + suffix,
+            "url": "url" + suffix,
+            "as_token": "as_token" + suffix,
+            "hs_token": "hs_token" + suffix,
+            "sender_localpart": "sender_localpart" + suffix,
+            "namespaces": {},
+        }
+        vals.update(kwargs)
+
+        _, path = tempfile.mkstemp(prefix="as_config")
+        with open(path, "w") as f:
+            f.write(yaml.dump(vals))
+        return path
+
+    @defer.inlineCallbacks
+    def test_unique_works(self):
+        f1 = self._write_config(suffix="1")
+        f2 = self._write_config(suffix="2")
+
+        config = Mock(app_service_config_files=[f1, f2])
+        hs = yield setup_test_homeserver(config=config)
+
+        ApplicationServiceStore(hs)
+
+    @defer.inlineCallbacks
+    def test_duplicate_ids(self):
+        f1 = self._write_config(id="id", suffix="1")
+        f2 = self._write_config(id="id", suffix="2")
+
+        config = Mock(app_service_config_files=[f1, f2])
+        hs = yield setup_test_homeserver(config=config)
+
+        with self.assertRaises(ConfigError) as cm:
+            ApplicationServiceStore(hs)
+
+        e = cm.exception
+        self.assertIn(f1, e.message)
+        self.assertIn(f2, e.message)
+        self.assertIn("id", e.message)
+
+    @defer.inlineCallbacks
+    def test_duplicate_as_tokens(self):
+        f1 = self._write_config(as_token="as_token", suffix="1")
+        f2 = self._write_config(as_token="as_token", suffix="2")
+
+        config = Mock(app_service_config_files=[f1, f2])
+        hs = yield setup_test_homeserver(config=config)
+
+        with self.assertRaises(ConfigError) as cm:
+            ApplicationServiceStore(hs)
+
+        e = cm.exception
+        self.assertIn(f1, e.message)
+        self.assertIn(f2, e.message)
+        self.assertIn("as_token", e.message)