Exemple #1
0
    def test_send_to_new_contact_failed_to_connect(self):
        """
        Sending a message to a new but unreachable contact results in the
        resulting deferred to be resolved with the expected exception.
        """
        nc = NetstringConnector(self.event_loop)
        contact = PeerNode(PUBLIC_KEY, self.version,
                           'netstring://192.168.0.1:1908')
        msg = OK('uuid', 'recipient', 'sender', 9999, 'version', 'seal')
        protocol = mock.MagicMock()

        def side_effect(*args, **kwargs):
            raise ValueError()

        protocol.send_string = mock.MagicMock(side_effect=side_effect)
        sender = Node(PUBLIC_KEY, PRIVATE_KEY, self.event_loop, nc, 1908)

        @asyncio.coroutine
        def faux_connect(protocol=protocol):
            return ('foo', protocol)

        with mock.patch.object(self.event_loop, 'create_connection',
                               return_value=faux_connect()):
            result = nc.send(contact, msg, sender)
            with self.assertRaises(ValueError) as ex:
                self.event_loop.run_until_complete(result)
            self.assertEqual(1, protocol.send_string.call_count)
            self.assertTrue(result.done())
            self.assertEqual(ex.exception, result.exception())
            self.assertNotIn(contact.network_id, nc._connections)
Exemple #2
0
 def test_receive_valid_json_invalid_message(self):
     """
     If a message is received that consists of valid json but a malformed
     message then log the incident for later analysis.
     """
     patcher = mock.patch('drogulus.net.netstring.log.error')
     nc = NetstringConnector(self.event_loop)
     ping = {
         'uuid': str(uuid.uuid4()),
         'recipient': PUBLIC_KEY,
         'sender': BAD_PUBLIC_KEY,
         'reply_port': 1908,
         'version': self.version,
     }
     seal = get_seal(ping, PRIVATE_KEY)
     ping['seal'] = seal
     ping['message'] = 'ping'
     raw = json.dumps(ping)
     sender = '192.168.0.1'
     handler = Node(PUBLIC_KEY, PRIVATE_KEY, self.event_loop, nc, 1908)
     protocol = mock.MagicMock()
     mock_log = patcher.start()
     nc.receive(raw, sender, handler, protocol)
     self.assertEqual(3, mock_log.call_count)
     patcher.stop()
Exemple #3
0
    def test_receive_valid_json_valid_message_from_old_peer(self):
        """
        A good message is received then the node handles the message as
        expected. The cached protocol object for the peer node is expired since
        a new protocol object is used in this instance.
        """
        nc = NetstringConnector(self.event_loop)
        old_protocol = mock.MagicMock()
        network_id = sha512(PUBLIC_KEY.encode('ascii')).hexdigest()
        nc._connections[network_id] = old_protocol

        ok = {
            'uuid': str(uuid.uuid4()),
            'recipient': PUBLIC_KEY,
            'sender': PUBLIC_KEY,
            'reply_port': 1908,
            'version': self.version,
        }
        seal = get_seal(ok, PRIVATE_KEY)
        ok['seal'] = seal
        ok['message'] = 'ok'
        raw = json.dumps(ok)
        sender = '192.168.0.1'
        handler = Node(PUBLIC_KEY, PRIVATE_KEY, self.event_loop, nc, 1908)
        handler.message_received = mock.MagicMock()
        protocol = mock.MagicMock()
        nc.receive(raw, sender, handler, protocol)
        self.assertIn(network_id, nc._connections)
        self.assertEqual(nc._connections[network_id], protocol)
        msg = from_dict(ok)
        handler.message_received.assert_called_once_with(msg, 'netstring',
                                                         sender,
                                                         msg.reply_port)
Exemple #4
0
    def test_send_to_new_contact_successful_connection(self):
        """
        Send a message to a new contact causes a new connection to be made
        whose associated protocol object is cached for later use.
        """
        nc = NetstringConnector(self.event_loop)
        contact = PeerNode(PUBLIC_KEY, self.version,
                           'netstring://192.168.0.1:1908')
        msg = OK('uuid', 'recipient', 'sender', 9999, 'version', 'seal')
        protocol = mock.MagicMock()
        protocol.send_string = mock.MagicMock()
        sender = Node(PUBLIC_KEY, PRIVATE_KEY, self.event_loop, nc, 1908)

        @asyncio.coroutine
        def faux_connect(protocol=protocol):
            return ('foo', protocol)

        with mock.patch.object(self.event_loop, 'create_connection',
                               return_value=faux_connect()):
            result = nc.send(contact, msg, sender)
            self.event_loop.run_until_complete(result)
            self.assertEqual(1, protocol.send_string.call_count)
            self.assertTrue(result.done())
            self.assertEqual(True, result.result())
            self.assertIn(contact.network_id, nc._connections)
            self.assertEqual(nc._connections[contact.network_id], protocol)
            expected = to_dict(msg)
            actual = json.loads(protocol.send_string.call_args[0][0])
            self.assertEqual(expected, actual)
Exemple #5
0
 def test_receive_invalid_json(self):
     """
     If a message is received that contains bad json then log the incident
     for later analysis.
     """
     patcher = mock.patch('drogulus.net.netstring.log.error')
     nc = NetstringConnector(self.event_loop)
     sender = '192.168.0.1'
     handler = Node(PUBLIC_KEY, PRIVATE_KEY, self.event_loop, nc, 1908)
     protocol = mock.MagicMock()
     raw = 'invalid JSON'
     mock_log = patcher.start()
     nc.receive(raw, sender, handler, protocol)
     self.assertEqual(3, mock_log.call_count)
     patcher.stop()
Exemple #6
0
 def test_init(self):
     """
     Check the class instantiates as expected.
     """
     nc = NetstringConnector(self.event_loop)
     self.assertEqual(nc._connections, {})
     self.assertEqual(nc.event_loop, self.event_loop)
Exemple #7
0
 def test_send_with_cached_protocol(self):
     """
     Send the message to the referenced contact using a cached protocol
     object.
     """
     nc = NetstringConnector(self.event_loop)
     nc._send_message_with_protocol = mock.MagicMock()
     contact = PeerNode(PUBLIC_KEY, self.version,
                        'netstring://192.168.0.1:1908')
     msg = OK('uuid', 'recipient', 'sender', 9999, 'version', 'seal')
     protocol = mock.MagicMock()
     sender = Node(PUBLIC_KEY, PRIVATE_KEY, self.event_loop, nc, 1908)
     nc._connections[contact.network_id] = protocol
     result = nc.send(contact, msg, sender)
     self.assertIsInstance(result, asyncio.Future)
     self.assertTrue(result.done())
     self.assertEqual(result.result(), True)
     nc._send_message_with_protocol.assert_called_once_with(msg, protocol)
Exemple #8
0
 def setUp(self):
     """
     A whole bunch of generic stuff we regularly need to faff about with
     that are set to some sane defaults.
     """
     loop = asyncio.new_event_loop()
     asyncio.set_event_loop(loop)
     self.event_loop = asyncio.get_event_loop()
     self.connector = NetstringConnector(self.event_loop)
     self.version = get_version()
Exemple #9
0
 def test_send_message_with_protocol(self):
     """
     Ensures that the message is translated into a dictionary and passed
     into the protocol object in the expected way.
     """
     nc = NetstringConnector(self.event_loop)
     protocol = mock.MagicMock()
     protocol.send_string = mock.MagicMock()
     msg = OK('uuid', 'recipient', 'sender', 9999, 'version', 'seal')
     nc._send_message_with_protocol(msg, protocol)
     expected = {
         'message': 'ok',
         'uuid': 'uuid',
         'recipient': 'recipient',
         'sender': 'sender',
         'reply_port': 9999,
         'version': 'version',
         'seal': 'seal'
     }
     actual = json.loads(protocol.send_string.call_args[0][0])
     self.assertEqual(expected, actual)
Exemple #10
0
    def test_send_with_failing_cached_protocol(self):
        """
        Attempting to send a message to the referenced contact using a
        cached protocol object that cannot send (e.g. perhaps the transport
        was dropped?) causes a retry as if the contact were new.
        """
        nc = NetstringConnector(self.event_loop)
        contact = PeerNode(PUBLIC_KEY, self.version,
                           'netstring://192.168.0.1:1908')
        msg = OK('uuid', 'recipient', 'sender', 9999, 'version', 'seal')
        protocol = mock.MagicMock()

        def side_effect(*args, **kwargs):
            raise ValueError()

        protocol.send_string = mock.MagicMock(side_effect=side_effect)
        nc._connections[contact.network_id] = protocol

        new_protocol = mock.MagicMock()
        new_protocol.send_string = mock.MagicMock()
        sender = Node(PUBLIC_KEY, PRIVATE_KEY, self.event_loop, nc, 1908)

        @asyncio.coroutine
        def faux_connect(protocol=new_protocol):
            return ('foo', protocol)

        with mock.patch.object(self.event_loop, 'create_connection',
                               return_value=faux_connect()):
            result = nc.send(contact, msg, sender)
            self.event_loop.run_until_complete(result)
            self.assertEqual(1, new_protocol.send_string.call_count)
            self.assertTrue(result.done())
            self.assertEqual(True, result.result())
            self.assertIn(contact.network_id, nc._connections)
            self.assertEqual(nc._connections[contact.network_id],
                             new_protocol)
            expected = to_dict(msg)
            actual = json.loads(protocol.send_string.call_args[0][0])
            self.assertEqual(expected, actual)
def start_node(event_loop, port):
    """
    Starts a local drogulus node using throw away keys, logging to the
    referenced directory and listening on the referenced port.

    Return the Process encapsulating this node.
    """
    connector = NetstringConnector(event_loop)
    private_key, public_key = get_keypair()
    instance = Drogulus(private_key, public_key, event_loop, connector, port)

    def protocol_factory(connector=connector, node=instance._node):
        """
        Returns an appropriately configured NetstringProtocol object for
        each connection.
        """
        return lambda: NetstringProtocol(connector, node)

    factory = protocol_factory()
    setup_server = event_loop.create_server(factory, port=port)
    event_loop.run_until_complete(setup_server)
    return instance