302 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			Python
		
	
	
			
		
		
	
	
			302 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			Python
		
	
	
| # Copyright 2014 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.
 | |
| 
 | |
| # trial imports
 | |
| from twisted.internet import defer
 | |
| from tests import unittest
 | |
| 
 | |
| # python imports
 | |
| from mock import Mock, ANY
 | |
| 
 | |
| from ..utils import MockHttpResource, MockClock, setup_test_homeserver
 | |
| 
 | |
| from synapse.federation import initialize_http_replication
 | |
| from synapse.events import FrozenEvent
 | |
| 
 | |
| 
 | |
| def make_pdu(prev_pdus=[], **kwargs):
 | |
|     """Provide some default fields for making a PduTuple."""
 | |
|     pdu_fields = {
 | |
|         "state_key": None,
 | |
|         "prev_events": prev_pdus,
 | |
|     }
 | |
|     pdu_fields.update(kwargs)
 | |
| 
 | |
|     return FrozenEvent(pdu_fields)
 | |
| 
 | |
| 
 | |
| class FederationTestCase(unittest.TestCase):
 | |
|     @defer.inlineCallbacks
 | |
|     def setUp(self):
 | |
|         self.mock_resource = MockHttpResource()
 | |
|         self.mock_http_client = Mock(spec=[
 | |
|             "get_json",
 | |
|             "put_json",
 | |
|         ])
 | |
|         self.mock_persistence = Mock(spec=[
 | |
|             "prep_send_transaction",
 | |
|             "delivered_txn",
 | |
|             "get_received_txn_response",
 | |
|             "set_received_txn_response",
 | |
|             "get_destination_retry_timings",
 | |
|             "get_auth_chain",
 | |
|         ])
 | |
|         self.mock_persistence.get_received_txn_response.return_value = (
 | |
|             defer.succeed(None)
 | |
|         )
 | |
| 
 | |
|         retry_timings_res = {
 | |
|             "destination": "",
 | |
|             "retry_last_ts": 0,
 | |
|             "retry_interval": 0,
 | |
|         }
 | |
|         self.mock_persistence.get_destination_retry_timings.return_value = (
 | |
|             defer.succeed(retry_timings_res)
 | |
|         )
 | |
|         self.mock_persistence.get_auth_chain.return_value = []
 | |
|         self.clock = MockClock()
 | |
|         hs = yield setup_test_homeserver(
 | |
|             resource_for_federation=self.mock_resource,
 | |
|             http_client=self.mock_http_client,
 | |
|             datastore=self.mock_persistence,
 | |
|             clock=self.clock,
 | |
|             keyring=Mock(),
 | |
|         )
 | |
|         self.federation = initialize_http_replication(hs)
 | |
|         self.distributor = hs.get_distributor()
 | |
| 
 | |
|     @defer.inlineCallbacks
 | |
|     def test_get_state(self):
 | |
|         mock_handler = Mock(spec=[
 | |
|             "get_state_for_pdu",
 | |
|         ])
 | |
| 
 | |
|         self.federation.set_handler(mock_handler)
 | |
| 
 | |
|         mock_handler.get_state_for_pdu.return_value = defer.succeed([])
 | |
| 
 | |
|         # Empty context initially
 | |
|         (code, response) = yield self.mock_resource.trigger(
 | |
|             "GET",
 | |
|             "/_matrix/federation/v1/state/my-context/",
 | |
|             None
 | |
|         )
 | |
|         self.assertEquals(200, code)
 | |
|         self.assertFalse(response["pdus"])
 | |
| 
 | |
|         # Now lets give the context some state
 | |
|         mock_handler.get_state_for_pdu.return_value = (
 | |
|             defer.succeed([
 | |
|                 make_pdu(
 | |
|                     event_id="the-pdu-id",
 | |
|                     origin="red",
 | |
|                     user_id="@a:red",
 | |
|                     room_id="my-context",
 | |
|                     type="m.topic",
 | |
|                     origin_server_ts=123456789000,
 | |
|                     depth=1,
 | |
|                     content={"topic": "The topic"},
 | |
|                     state_key="",
 | |
|                     power_level=1000,
 | |
|                     prev_state="last-pdu-id",
 | |
|                 ),
 | |
|             ])
 | |
|         )
 | |
| 
 | |
|         (code, response) = yield self.mock_resource.trigger(
 | |
|             "GET",
 | |
|             "/_matrix/federation/v1/state/my-context/",
 | |
|             None
 | |
|         )
 | |
|         self.assertEquals(200, code)
 | |
|         self.assertEquals(1, len(response["pdus"]))
 | |
| 
 | |
|     @defer.inlineCallbacks
 | |
|     def test_get_pdu(self):
 | |
|         mock_handler = Mock(spec=[
 | |
|             "get_persisted_pdu",
 | |
|         ])
 | |
| 
 | |
|         self.federation.set_handler(mock_handler)
 | |
| 
 | |
|         mock_handler.get_persisted_pdu.return_value = (
 | |
|             defer.succeed(None)
 | |
|         )
 | |
| 
 | |
|         (code, response) = yield self.mock_resource.trigger(
 | |
|             "GET",
 | |
|             "/_matrix/federation/v1/event/abc123def456/",
 | |
|             None
 | |
|         )
 | |
|         self.assertEquals(404, code)
 | |
| 
 | |
|         # Now insert such a PDU
 | |
|         mock_handler.get_persisted_pdu.return_value = (
 | |
|             defer.succeed(
 | |
|                 make_pdu(
 | |
|                     event_id="abc123def456",
 | |
|                     origin="red",
 | |
|                     user_id="@a:red",
 | |
|                     room_id="my-context",
 | |
|                     type="m.text",
 | |
|                     origin_server_ts=123456789001,
 | |
|                     depth=1,
 | |
|                     content={"text": "Here is the message"},
 | |
|                 )
 | |
|             )
 | |
|         )
 | |
| 
 | |
|         (code, response) = yield self.mock_resource.trigger(
 | |
|             "GET",
 | |
|             "/_matrix/federation/v1/event/abc123def456/",
 | |
|             None
 | |
|         )
 | |
|         self.assertEquals(200, code)
 | |
|         self.assertEquals(1, len(response["pdus"]))
 | |
|         self.assertEquals("m.text", response["pdus"][0]["type"])
 | |
| 
 | |
|     @defer.inlineCallbacks
 | |
|     def test_send_pdu(self):
 | |
|         self.mock_http_client.put_json.return_value = defer.succeed(
 | |
|             (200, "OK")
 | |
|         )
 | |
| 
 | |
|         pdu = make_pdu(
 | |
|             event_id="abc123def456",
 | |
|             origin="red",
 | |
|             user_id="@a:red",
 | |
|             room_id="my-context",
 | |
|             type="m.text",
 | |
|             origin_server_ts=123456789001,
 | |
|             depth=1,
 | |
|             content={"text": "Here is the message"},
 | |
|         )
 | |
| 
 | |
|         yield self.federation.send_pdu(pdu, ["remote"])
 | |
| 
 | |
|         self.mock_http_client.put_json.assert_called_with(
 | |
|             "remote",
 | |
|             path="/_matrix/federation/v1/send/1000000/",
 | |
|             data={
 | |
|                 "origin_server_ts": 1000000,
 | |
|                 "origin": "test",
 | |
|                 "pdus": [
 | |
|                     pdu.get_pdu_json(),
 | |
|                 ],
 | |
|                 'pdu_failures': [],
 | |
|             },
 | |
|             json_data_callback=ANY,
 | |
|         )
 | |
| 
 | |
|     @defer.inlineCallbacks
 | |
|     def test_send_edu(self):
 | |
|         self.mock_http_client.put_json.return_value = defer.succeed(
 | |
|             (200, "OK")
 | |
|         )
 | |
| 
 | |
|         yield self.federation.send_edu(
 | |
|             destination="remote",
 | |
|             edu_type="m.test",
 | |
|             content={"testing": "content here"},
 | |
|         )
 | |
| 
 | |
|         # MockClock ensures we can guess these timestamps
 | |
|         self.mock_http_client.put_json.assert_called_with(
 | |
|             "remote",
 | |
|             path="/_matrix/federation/v1/send/1000000/",
 | |
|             data={
 | |
|                 "origin": "test",
 | |
|                 "origin_server_ts": 1000000,
 | |
|                 "pdus": [],
 | |
|                 "edus": [
 | |
|                     {
 | |
|                         "edu_type": "m.test",
 | |
|                         "content": {"testing": "content here"},
 | |
|                     }
 | |
|                 ],
 | |
|                 'pdu_failures': [],
 | |
|             },
 | |
|             json_data_callback=ANY,
 | |
|         )
 | |
| 
 | |
|     @defer.inlineCallbacks
 | |
|     def test_recv_edu(self):
 | |
|         recv_observer = Mock()
 | |
|         recv_observer.return_value = defer.succeed(())
 | |
| 
 | |
|         self.federation.register_edu_handler("m.test", recv_observer)
 | |
| 
 | |
|         yield self.mock_resource.trigger(
 | |
|             "PUT",
 | |
|             "/_matrix/federation/v1/send/1001000/",
 | |
|             """{
 | |
|                 "origin": "remote",
 | |
|                 "origin_server_ts": 1001000,
 | |
|                 "pdus": [],
 | |
|                 "edus": [
 | |
|                     {
 | |
|                         "origin": "remote",
 | |
|                         "destination": "test",
 | |
|                         "edu_type": "m.test",
 | |
|                         "content": {"testing": "reply here"}
 | |
|                     }
 | |
|                 ]
 | |
|             }"""
 | |
|         )
 | |
| 
 | |
|         recv_observer.assert_called_with(
 | |
|             "remote", {"testing": "reply here"}
 | |
|         )
 | |
| 
 | |
|     @defer.inlineCallbacks
 | |
|     def test_send_query(self):
 | |
|         self.mock_http_client.get_json.return_value = defer.succeed(
 | |
|             {"your": "response"}
 | |
|         )
 | |
| 
 | |
|         response = yield self.federation.make_query(
 | |
|             destination="remote",
 | |
|             query_type="a-question",
 | |
|             args={"one": "1", "two": "2"},
 | |
|         )
 | |
| 
 | |
|         self.assertEquals({"your": "response"}, response)
 | |
| 
 | |
|         self.mock_http_client.get_json.assert_called_with(
 | |
|             destination="remote",
 | |
|             path="/_matrix/federation/v1/query/a-question",
 | |
|             args={"one": "1", "two": "2"},
 | |
|             retry_on_dns_fail=True,
 | |
|         )
 | |
| 
 | |
|     @defer.inlineCallbacks
 | |
|     def test_recv_query(self):
 | |
|         recv_handler = Mock()
 | |
|         recv_handler.return_value = defer.succeed({"another": "response"})
 | |
| 
 | |
|         self.federation.register_query_handler("a-question", recv_handler)
 | |
| 
 | |
|         code, response = yield self.mock_resource.trigger(
 | |
|             "GET",
 | |
|             "/_matrix/federation/v1/query/a-question?three=3&four=4",
 | |
|             None
 | |
|         )
 | |
| 
 | |
|         self.assertEquals(200, code)
 | |
|         self.assertEquals({"another": "response"}, response)
 | |
| 
 | |
|         recv_handler.assert_called_with(
 | |
|             {"three": "3", "four": "4"}
 | |
|         )
 |