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)
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)
def test_ok(self): """ Expected behaviour of a ok message. """ ok = OK(self.uuid, self.recipient, self.sender, self.reply_port, self.version, self.seal) self.assertEqual(self.uuid, ok.uuid) self.assertEqual(self.recipient, ok.recipient) self.assertEqual(self.sender, ok.sender) self.assertEqual(self.reply_port, ok.reply_port) self.assertEqual(self.version, ok.version) self.assertEqual(self.seal, ok.seal)
def test_check_seal_bad_seal(self): """ Ensure a message with a bad seal (i.e. malformed junk) fails the check. """ ok_dict = { 'uuid': str(uuid.uuid4()), 'recipient': PUBLIC_KEY, 'sender': BAD_PUBLIC_KEY, 'reply_port': 1908, 'version': get_version() } seal = 'not a seal' ok = OK(ok_dict['uuid'], ok_dict['recipient'], ok_dict['sender'], ok_dict['reply_port'], ok_dict['version'], seal) self.assertFalse(check_seal(ok))
def test_check_seal_invalid_seal(self): """ Ensure a message with an invalid seal fails the check. """ ok_dict = { 'uuid': str(uuid.uuid4()), 'recipient': PUBLIC_KEY, 'sender': BAD_PUBLIC_KEY, 'reply_port': 1908, 'version': get_version() } seal = get_seal(ok_dict, PRIVATE_KEY) ok = OK(ok_dict['uuid'], ok_dict['recipient'], ok_dict['sender'], ok_dict['reply_port'], ok_dict['version'], seal) self.assertFalse(check_seal(ok))
def test_check_seal(self): """ Make sure message objects that contain a valid seal are correctly checked. """ ok_dict = { 'uuid': str(uuid.uuid4()), 'recipient': PUBLIC_KEY, 'sender': PUBLIC_KEY, 'reply_port': 1908, 'version': get_version() } seal = get_seal(ok_dict, PRIVATE_KEY) ok = OK(ok_dict['uuid'], ok_dict['recipient'], ok_dict['sender'], ok_dict['reply_port'], ok_dict['version'], seal) self.assertTrue(check_seal(ok))
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)
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)
def test_send(self): """ Test the good case. We should end up with a task wrapping an appropriate call to aiohttp.request. """ contact = PeerNode(PUBLIC_KEY, self.version, 'http://192.168.0.1:80') msg = OK('uuid', 'recipient', 'sender', 9999, 'version', 'seal') msg_json = json.dumps(to_dict(msg)) headers = {'content-type': 'application/json'} connector = HttpConnector(self.event_loop) @asyncio.coroutine def faux_request(*args, **kwargs): return 'foo' with mock.patch.object(aiohttp, 'request', return_value=faux_request()) as request: result = connector.send(contact, msg) self.assertIsInstance(result, asyncio.Task) request.assert_called_once_with('post', contact.uri, data=msg_json, headers=headers)
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 test_handle_response_wrong_message_type(self): """ Ensure that a response that isn't a Nodes or Value message results in the responding peer node being blacklisted and the error being correctly handled. """ lookup = Lookup(FindNode, self.target, self.node, self.event_loop) uuid = [uuid for uuid in lookup.pending_requests.keys()][0] contact = lookup.shortlist[0] msg = OK(uuid, self.node.network_id, self.node.network_id, self.reply_port, self.version, self.seal) response = asyncio.Future() response.set_result(msg) lookup._blacklist = mock.MagicMock() lookup._handle_error = mock.MagicMock() lookup._handle_response(uuid, contact, response) lookup._blacklist.assert_called_once_with(contact) self.assertEqual(lookup._handle_error.call_count, 1) args = lookup._handle_error.call_args[0] self.assertEqual(args[0], uuid) self.assertEqual(args[1], contact) self.assertIsInstance(args[2], TypeError) self.assertEqual(args[2].args[0], "Unexpected response type from {}".format(contact))
def faux_receive(*args, **kwargs): return OK('uuid', 'recipient', 'sender', 9999, 'version', 'seal')