test_account_data_manager.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. # Copyright 2022 The Matrix.org Foundation C.I.C.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. from twisted.test.proto_helpers import MemoryReactor
  15. from synapse.api.errors import SynapseError
  16. from synapse.rest import admin
  17. from synapse.server import HomeServer
  18. from synapse.util import Clock
  19. from tests.unittest import HomeserverTestCase
  20. class ModuleApiTestCase(HomeserverTestCase):
  21. servlets = [
  22. admin.register_servlets,
  23. ]
  24. def prepare(
  25. self, reactor: MemoryReactor, clock: Clock, homeserver: HomeServer
  26. ) -> None:
  27. self._store = homeserver.get_datastores().main
  28. self._module_api = homeserver.get_module_api()
  29. self._account_data_mgr = self._module_api.account_data_manager
  30. self.user_id = self.register_user("kristina", "secret")
  31. def test_get_global(self) -> None:
  32. """
  33. Tests that getting global account data through the module API works as
  34. expected, including getting `None` for unset account data.
  35. """
  36. self.get_success(
  37. self._store.add_account_data_for_user(
  38. self.user_id, "test.data", {"wombat": True}
  39. )
  40. )
  41. # Getting existent account data works as expected.
  42. self.assertEqual(
  43. self.get_success(
  44. self._account_data_mgr.get_global(self.user_id, "test.data")
  45. ),
  46. {"wombat": True},
  47. )
  48. # Getting non-existent account data returns None.
  49. self.assertIsNone(
  50. self.get_success(
  51. self._account_data_mgr.get_global(self.user_id, "no.data.at.all")
  52. )
  53. )
  54. def test_get_global_validation(self) -> None:
  55. """
  56. Tests that invalid or remote user IDs are treated as errors and raised as exceptions,
  57. whilst getting global account data for a user.
  58. This is a design choice to try and communicate potential bugs to modules
  59. earlier on.
  60. """
  61. with self.assertRaises(SynapseError):
  62. self.get_success_or_raise(
  63. self._account_data_mgr.get_global("this isn't a user id", "test.data")
  64. )
  65. with self.assertRaises(ValueError):
  66. self.get_success_or_raise(
  67. self._account_data_mgr.get_global("@valid.but:remote", "test.data")
  68. )
  69. def test_get_global_no_mutability(self) -> None:
  70. """
  71. Tests that modules can't introduce bugs into Synapse by mutating the result
  72. of `get_global`.
  73. """
  74. # First add some account data to set up the test.
  75. self.get_success(
  76. self._store.add_account_data_for_user(
  77. self.user_id, "test.data", {"wombat": True}
  78. )
  79. )
  80. # Now request that data and then mutate it (out of negligence or otherwise).
  81. the_data = self.get_success(
  82. self._account_data_mgr.get_global(self.user_id, "test.data")
  83. )
  84. with self.assertRaises(TypeError):
  85. # This throws an exception because it's a frozen dict.
  86. the_data["wombat"] = False # type: ignore[index]
  87. def test_put_global(self) -> None:
  88. """
  89. Tests that written account data using `put_global` can be read out again later.
  90. """
  91. self.get_success(
  92. self._module_api.account_data_manager.put_global(
  93. self.user_id, "test.data", {"wombat": True}
  94. )
  95. )
  96. # Request that account data from the normal store; check it's as we expect.
  97. self.assertEqual(
  98. self.get_success(
  99. self._store.get_global_account_data_by_type_for_user(
  100. self.user_id, "test.data"
  101. )
  102. ),
  103. {"wombat": True},
  104. )
  105. def test_put_global_validation(self) -> None:
  106. """
  107. Tests that a module can't write account data to user IDs that don't have
  108. actual users registered to them.
  109. Modules also must supply the correct types.
  110. """
  111. with self.assertRaises(SynapseError):
  112. self.get_success_or_raise(
  113. self._account_data_mgr.put_global(
  114. "this isn't a user id", "test.data", {}
  115. )
  116. )
  117. with self.assertRaises(ValueError):
  118. self.get_success_or_raise(
  119. self._account_data_mgr.put_global("@valid.but:remote", "test.data", {})
  120. )
  121. with self.assertRaises(ValueError):
  122. self.get_success_or_raise(
  123. self._module_api.account_data_manager.put_global(
  124. "@notregistered:test", "test.data", {}
  125. )
  126. )
  127. with self.assertRaises(TypeError):
  128. # The account data type must be a string.
  129. self.get_success_or_raise(
  130. self._module_api.account_data_manager.put_global(self.user_id, 42, {}) # type: ignore[arg-type]
  131. )
  132. with self.assertRaises(TypeError):
  133. # The account data dict must be a dict.
  134. # noinspection PyTypeChecker
  135. self.get_success_or_raise(
  136. self._module_api.account_data_manager.put_global(
  137. self.user_id, "test.data", 42 # type: ignore[arg-type]
  138. )
  139. )