test_federation.py 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. # Copyright 2014 OpenMarket Ltd
  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. # trial imports
  15. from twisted.internet import defer
  16. from tests import unittest
  17. # python imports
  18. from mock import Mock, ANY
  19. from ..utils import MockHttpResource, MockClock, MockKey
  20. from synapse.server import HomeServer
  21. from synapse.federation import initialize_http_replication
  22. from synapse.federation.units import Pdu
  23. from synapse.storage.pdu import PduTuple, PduEntry
  24. def make_pdu(prev_pdus=[], **kwargs):
  25. """Provide some default fields for making a PduTuple."""
  26. pdu_fields = {
  27. "is_state": False,
  28. "unrecognized_keys": [],
  29. "outlier": False,
  30. "have_processed": True,
  31. "state_key": None,
  32. "power_level": None,
  33. "prev_state_id": None,
  34. "prev_state_origin": None,
  35. }
  36. pdu_fields.update(kwargs)
  37. return PduTuple(PduEntry(**pdu_fields), prev_pdus)
  38. class FederationTestCase(unittest.TestCase):
  39. def setUp(self):
  40. self.mock_resource = MockHttpResource()
  41. self.mock_http_client = Mock(spec=[
  42. "get_json",
  43. "put_json",
  44. ])
  45. self.mock_persistence = Mock(spec=[
  46. "get_current_state_for_context",
  47. "get_pdu",
  48. "persist_event",
  49. "update_min_depth_for_context",
  50. "prep_send_transaction",
  51. "delivered_txn",
  52. "get_received_txn_response",
  53. "set_received_txn_response",
  54. ])
  55. self.mock_persistence.get_received_txn_response.return_value = (
  56. defer.succeed(None)
  57. )
  58. self.mock_config = Mock()
  59. self.mock_config.signing_key = [MockKey()]
  60. self.clock = MockClock()
  61. hs = HomeServer("test",
  62. resource_for_federation=self.mock_resource,
  63. http_client=self.mock_http_client,
  64. db_pool=None,
  65. datastore=self.mock_persistence,
  66. clock=self.clock,
  67. config=self.mock_config,
  68. keyring=Mock(),
  69. )
  70. self.federation = initialize_http_replication(hs)
  71. self.distributor = hs.get_distributor()
  72. @defer.inlineCallbacks
  73. def test_get_state(self):
  74. self.mock_persistence.get_current_state_for_context.return_value = (
  75. defer.succeed([])
  76. )
  77. # Empty context initially
  78. (code, response) = yield self.mock_resource.trigger("GET",
  79. "/_matrix/federation/v1/state/my-context/", None)
  80. self.assertEquals(200, code)
  81. self.assertFalse(response["pdus"])
  82. # Now lets give the context some state
  83. self.mock_persistence.get_current_state_for_context.return_value = (
  84. defer.succeed([
  85. make_pdu(
  86. pdu_id="the-pdu-id",
  87. origin="red",
  88. context="my-context",
  89. pdu_type="m.topic",
  90. ts=123456789000,
  91. depth=1,
  92. is_state=True,
  93. content_json='{"topic":"The topic"}',
  94. state_key="",
  95. power_level=1000,
  96. prev_state_id="last-pdu-id",
  97. prev_state_origin="blue",
  98. ),
  99. ])
  100. )
  101. (code, response) = yield self.mock_resource.trigger("GET",
  102. "/_matrix/federation/v1/state/my-context/", None)
  103. self.assertEquals(200, code)
  104. self.assertEquals(1, len(response["pdus"]))
  105. @defer.inlineCallbacks
  106. def test_get_pdu(self):
  107. self.mock_persistence.get_pdu.return_value = (
  108. defer.succeed(None)
  109. )
  110. (code, response) = yield self.mock_resource.trigger("GET",
  111. "/_matrix/federation/v1/pdu/red/abc123def456/", None)
  112. self.assertEquals(404, code)
  113. # Now insert such a PDU
  114. self.mock_persistence.get_pdu.return_value = (
  115. defer.succeed(
  116. make_pdu(
  117. pdu_id="abc123def456",
  118. origin="red",
  119. context="my-context",
  120. pdu_type="m.text",
  121. ts=123456789001,
  122. depth=1,
  123. content_json='{"text":"Here is the message"}',
  124. )
  125. )
  126. )
  127. (code, response) = yield self.mock_resource.trigger("GET",
  128. "/_matrix/federation/v1/pdu/red/abc123def456/", None)
  129. self.assertEquals(200, code)
  130. self.assertEquals(1, len(response["pdus"]))
  131. self.assertEquals("m.text", response["pdus"][0]["pdu_type"])
  132. @defer.inlineCallbacks
  133. def test_send_pdu(self):
  134. self.mock_http_client.put_json.return_value = defer.succeed(
  135. (200, "OK")
  136. )
  137. pdu = Pdu(
  138. pdu_id="abc123def456",
  139. origin="red",
  140. destinations=["remote"],
  141. context="my-context",
  142. origin_server_ts=123456789002,
  143. pdu_type="m.test",
  144. content={"testing": "content here"},
  145. depth=1,
  146. )
  147. yield self.federation.send_pdu(pdu)
  148. self.mock_http_client.put_json.assert_called_with(
  149. "remote",
  150. path="/_matrix/federation/v1/send/1000000/",
  151. data={
  152. "origin_server_ts": 1000000,
  153. "origin": "test",
  154. "pdus": [
  155. {
  156. "origin": "red",
  157. "pdu_id": "abc123def456",
  158. "prev_pdus": [],
  159. "origin_server_ts": 123456789002,
  160. "context": "my-context",
  161. "pdu_type": "m.test",
  162. "is_state": False,
  163. "content": {"testing": "content here"},
  164. "depth": 1,
  165. },
  166. ]
  167. },
  168. json_data_callback=ANY,
  169. )
  170. @defer.inlineCallbacks
  171. def test_send_edu(self):
  172. self.mock_http_client.put_json.return_value = defer.succeed(
  173. (200, "OK")
  174. )
  175. yield self.federation.send_edu(
  176. destination="remote",
  177. edu_type="m.test",
  178. content={"testing": "content here"},
  179. )
  180. # MockClock ensures we can guess these timestamps
  181. self.mock_http_client.put_json.assert_called_with(
  182. "remote",
  183. path="/_matrix/federation/v1/send/1000000/",
  184. data={
  185. "origin": "test",
  186. "origin_server_ts": 1000000,
  187. "pdus": [],
  188. "edus": [
  189. {
  190. # TODO: SYN-103: Remove "origin" and "destination"
  191. "origin": "test",
  192. "destination": "remote",
  193. "edu_type": "m.test",
  194. "content": {"testing": "content here"},
  195. }
  196. ],
  197. },
  198. json_data_callback=ANY,
  199. )
  200. @defer.inlineCallbacks
  201. def test_recv_edu(self):
  202. recv_observer = Mock()
  203. recv_observer.return_value = defer.succeed(())
  204. self.federation.register_edu_handler("m.test", recv_observer)
  205. yield self.mock_resource.trigger("PUT",
  206. "/_matrix/federation/v1/send/1001000/",
  207. """{
  208. "origin": "remote",
  209. "origin_server_ts": 1001000,
  210. "pdus": [],
  211. "edus": [
  212. {
  213. "origin": "remote",
  214. "destination": "test",
  215. "edu_type": "m.test",
  216. "content": {"testing": "reply here"}
  217. }
  218. ]
  219. }""")
  220. recv_observer.assert_called_with(
  221. "remote", {"testing": "reply here"}
  222. )
  223. @defer.inlineCallbacks
  224. def test_send_query(self):
  225. self.mock_http_client.get_json.return_value = defer.succeed(
  226. {"your": "response"}
  227. )
  228. response = yield self.federation.make_query(
  229. destination="remote",
  230. query_type="a-question",
  231. args={"one": "1", "two": "2"},
  232. )
  233. self.assertEquals({"your": "response"}, response)
  234. self.mock_http_client.get_json.assert_called_with(
  235. destination="remote",
  236. path="/_matrix/federation/v1/query/a-question",
  237. args={"one": "1", "two": "2"},
  238. retry_on_dns_fail=True,
  239. )
  240. @defer.inlineCallbacks
  241. def test_recv_query(self):
  242. recv_handler = Mock()
  243. recv_handler.return_value = defer.succeed({"another": "response"})
  244. self.federation.register_query_handler("a-question", recv_handler)
  245. code, response = yield self.mock_resource.trigger("GET",
  246. "/_matrix/federation/v1/query/a-question?three=3&four=4", None)
  247. self.assertEquals(200, code)
  248. self.assertEquals({"another": "response"}, response)
  249. recv_handler.assert_called_with(
  250. {"three": "3", "four": "4"}
  251. )