lbry-sdk/lbrynet/tests/unit/dht/test_protocol.py

201 lines
8.2 KiB
Python
Raw Normal View History

2015-08-20 17:27:15 +02:00
import time
import unittest
from twisted.internet.task import Clock
2018-03-27 23:35:31 +02:00
from twisted.internet import defer
2015-08-20 17:27:15 +02:00
import lbrynet.dht.protocol
import lbrynet.dht.contact
import lbrynet.dht.constants
import lbrynet.dht.msgtypes
2017-10-10 19:31:18 +02:00
from lbrynet.dht.error import TimeoutError
from lbrynet.dht.node import Node, rpcmethod
from lbrynet.tests.mocks import listenUDP, resolve
2018-02-22 17:29:10 +01:00
from lbrynet.core.call_later_manager import CallLaterManager
import logging
log = logging.getLogger()
2017-10-10 19:31:18 +02:00
2015-08-20 17:27:15 +02:00
class KademliaProtocolTest(unittest.TestCase):
""" Test case for the Protocol class """
2017-10-10 19:31:18 +02:00
udpPort = 9182
2015-08-20 17:27:15 +02:00
def setUp(self):
self._reactor = Clock()
2018-02-22 17:29:10 +01:00
CallLaterManager.setup(self._reactor.callLater)
self.node = Node(node_id='1' * 48, udpPort=self.udpPort, externalIP="127.0.0.1", listenUDP=listenUDP,
resolve=resolve, clock=self._reactor, callLater=self._reactor.callLater)
def tearDown(self):
2018-02-22 17:29:10 +01:00
CallLaterManager.stop()
del self._reactor
2015-08-20 17:27:15 +02:00
@defer.inlineCallbacks
2015-08-20 17:27:15 +02:00
def testReactor(self):
""" Tests if the reactor can start/stop the protocol correctly """
d = defer.Deferred()
self._reactor.callLater(1, d.callback, True)
self._reactor.advance(1)
result = yield d
self.assertTrue(result)
2015-08-20 17:27:15 +02:00
def testRPCTimeout(self):
""" Tests if a RPC message sent to a dead remote node times out correctly """
dead_node = Node(node_id='2' * 48, udpPort=self.udpPort, externalIP="127.0.0.2", listenUDP=listenUDP,
resolve=resolve, clock=self._reactor, callLater=self._reactor.callLater)
dead_node.start_listening()
dead_node.stop()
self._reactor.pump([1 for _ in range(10)])
dead_contact = lbrynet.dht.contact.Contact('2' * 48, '127.0.0.2', 9182, self.node._protocol)
self.node.addContact(dead_contact)
2017-10-10 19:31:18 +02:00
@rpcmethod
def fake_ping(*args, **kwargs):
time.sleep(lbrynet.dht.constants.rpcTimeout + 1)
return 'pong'
real_ping = self.node.ping
real_timeout = lbrynet.dht.constants.rpcTimeout
real_attempts = lbrynet.dht.constants.rpcAttempts
lbrynet.dht.constants.rpcAttempts = 1
lbrynet.dht.constants.rpcTimeout = 1
2017-10-10 19:31:18 +02:00
self.node.ping = fake_ping
2015-08-20 17:27:15 +02:00
# Make sure the contact was added
self.failIf(dead_contact not in self.node.contacts,
2017-10-10 19:31:18 +02:00
'Contact not added to fake node (error in test code)')
self.node.start_listening()
2017-10-10 19:31:18 +02:00
# Run the PING RPC (which should raise a timeout error)
df = self.node._protocol.sendRPC(dead_contact, 'ping', {})
2017-10-10 19:31:18 +02:00
def check_timeout(err):
self.assertEqual(err.type, TimeoutError)
2017-10-10 19:31:18 +02:00
df.addErrback(check_timeout)
def reset_values():
self.node.ping = real_ping
lbrynet.dht.constants.rpcTimeout = real_timeout
lbrynet.dht.constants.rpcAttempts = real_attempts
# See if the contact was removed due to the timeout
def check_removed_contact():
self.failIf(dead_contact in self.node.contacts,
2017-10-10 19:31:18 +02:00
'Contact was not removed after RPC timeout; check exception types.')
df.addCallback(lambda _: reset_values())
2015-08-20 17:27:15 +02:00
# Stop the reactor if a result arrives (timeout or not)
2017-10-10 19:31:18 +02:00
df.addCallback(lambda _: check_removed_contact())
self._reactor.pump([1 for _ in range(20)])
2017-10-10 19:31:18 +02:00
2015-08-20 17:27:15 +02:00
def testRPCRequest(self):
""" Tests if a valid RPC request is executed and responded to correctly """
remote_node = Node(node_id='2' * 48, udpPort=self.udpPort, externalIP="127.0.0.2", listenUDP=listenUDP,
resolve=resolve, clock=self._reactor, callLater=self._reactor.callLater)
remote_node.start_listening()
remoteContact = lbrynet.dht.contact.Contact('2' * 48, '127.0.0.2', 9182, self.node._protocol)
2015-08-20 17:27:15 +02:00
self.node.addContact(remoteContact)
2015-08-20 17:27:15 +02:00
self.error = None
2017-10-10 19:31:18 +02:00
2015-08-20 17:27:15 +02:00
def handleError(f):
self.error = 'An RPC error occurred: %s' % f.getErrorMessage()
2017-10-10 19:31:18 +02:00
2015-08-20 17:27:15 +02:00
def handleResult(result):
expectedResult = 'pong'
if result != expectedResult:
2017-10-10 19:31:18 +02:00
self.error = 'Result from RPC is incorrect; expected "%s", got "%s"' \
% (expectedResult, result)
# Publish the "local" node on the network
self.node.start_listening()
2015-08-20 17:27:15 +02:00
# Simulate the RPC
df = remoteContact.ping()
df.addCallback(handleResult)
df.addErrback(handleError)
for _ in range(10):
self._reactor.advance(1)
2015-08-20 17:27:15 +02:00
self.failIf(self.error, self.error)
# The list of sent RPC messages should be empty at this stage
self.failUnlessEqual(len(self.node._protocol._sentMessages), 0,
2017-10-10 19:31:18 +02:00
'The protocol is still waiting for a RPC result, '
'but the transaction is already done!')
2015-08-20 17:27:15 +02:00
def testRPCAccess(self):
""" Tests invalid RPC requests
Verifies that a RPC request for an existing but unpublished
method is denied, and that the associated (remote) exception gets
raised locally """
remote_node = Node(node_id='2' * 48, udpPort=self.udpPort, externalIP="127.0.0.2", listenUDP=listenUDP,
resolve=resolve, clock=self._reactor, callLater=self._reactor.callLater)
remote_node.start_listening()
remote_contact = lbrynet.dht.contact.Contact('2' * 48, '127.0.0.2', 9182, self.node._protocol)
self.node.addContact(remote_contact)
2015-08-20 17:27:15 +02:00
self.error = None
2017-10-10 19:31:18 +02:00
2015-08-20 17:27:15 +02:00
def handleError(f):
try:
f.raiseException()
except AttributeError, e:
# This is the expected outcome since the remote node did not publish the method
self.error = None
except Exception, e:
2017-10-10 19:31:18 +02:00
self.error = 'The remote method failed, but the wrong exception was raised; ' \
'expected AttributeError, got %s' % type(e)
2015-08-20 17:27:15 +02:00
def handleResult(result):
2017-10-10 19:31:18 +02:00
self.error = 'The remote method executed successfully, returning: "%s"; ' \
'this RPC should not have been allowed.' % result
self.node.start_listening()
self._reactor.pump([1 for _ in range(10)])
2015-08-20 17:27:15 +02:00
# Simulate the RPC
df = remote_contact.not_a_rpc_function()
2015-08-20 17:27:15 +02:00
df.addCallback(handleResult)
df.addErrback(handleError)
self._reactor.pump([1 for _ in range(10)])
2015-08-20 17:27:15 +02:00
self.failIf(self.error, self.error)
# The list of sent RPC messages should be empty at this stage
self.failUnlessEqual(len(self.node._protocol._sentMessages), 0,
2017-10-10 19:31:18 +02:00
'The protocol is still waiting for a RPC result, '
'but the transaction is already done!')
2015-08-20 17:27:15 +02:00
def testRPCRequestArgs(self):
""" Tests if an RPC requiring arguments is executed correctly """
remote_node = Node(node_id='2' * 48, udpPort=self.udpPort, externalIP="127.0.0.2", listenUDP=listenUDP,
resolve=resolve, clock=self._reactor, callLater=self._reactor.callLater)
remote_node.start_listening()
remote_contact = lbrynet.dht.contact.Contact('2' * 48, '127.0.0.2', 9182, self.node._protocol)
self.node.addContact(remote_contact)
2015-08-20 17:27:15 +02:00
self.error = None
2017-10-10 19:31:18 +02:00
2015-08-20 17:27:15 +02:00
def handleError(f):
self.error = 'An RPC error occurred: %s' % f.getErrorMessage()
2017-10-10 19:31:18 +02:00
2015-08-20 17:27:15 +02:00
def handleResult(result):
2017-10-10 19:31:18 +02:00
expectedResult = 'pong'
if result != expectedResult:
self.error = 'Result from RPC is incorrect; expected "%s", got "%s"' % \
(expectedResult, result)
# Publish the "local" node on the network
self.node.start_listening()
2015-08-20 17:27:15 +02:00
# Simulate the RPC
df = remote_contact.ping()
2015-08-20 17:27:15 +02:00
df.addCallback(handleResult)
df.addErrback(handleError)
self._reactor.pump([1 for _ in range(10)])
2015-08-20 17:27:15 +02:00
self.failIf(self.error, self.error)
# The list of sent RPC messages should be empty at this stage
self.failUnlessEqual(len(self.node._protocol._sentMessages), 0,
2017-10-10 19:31:18 +02:00
'The protocol is still waiting for a RPC result, '
'but the transaction is already done!')