async def test_connection_properties(): Protocol_A = ProtocolFactory() Protocol_B = ProtocolFactory() alice_handshakers = (NoopHandshaker(Protocol_A), ) bob_handshakers = (NoopHandshaker(Protocol_A), NoopHandshaker(Protocol_B)) bob_capabilities = (Protocol_A.as_capability(), Protocol_B.as_capability()) bob_remote = NodeFactory() bob_private_key = PrivateKeyFactory() pair_factory = ConnectionPairFactory( alice_handshakers=alice_handshakers, bob_handshakers=bob_handshakers, alice_p2p_version=DEVP2P_V4, bob_client_version='bob-client', bob_p2p_version=DEVP2P_V5, bob_private_key=bob_private_key, bob_remote=bob_remote, ) async with pair_factory as (connection, _): assert type(connection.get_base_protocol()) is P2PProtocolV4 assert connection.remote_capabilities == bob_capabilities assert connection.remote_p2p_version == DEVP2P_V5 assert connection.negotiated_p2p_version == DEVP2P_V4 assert connection.remote_public_key == bob_private_key.public_key assert connection.client_version_string == 'bob-client' assert connection.safe_client_version_string == 'bob-client'
async def test_connection_pair_factory_no_protocols_with_different_p2p_versions( alice_p2p_version, bob_p2p_version, ): pair_factory = ConnectionPairFactory( alice_p2p_version=alice_p2p_version, bob_p2p_version=bob_p2p_version, ) async with pair_factory as (alice_connection, bob_connection): expected_base_protocol_version = min(alice_p2p_version, bob_p2p_version) if expected_base_protocol_version == DEVP2P_V4: expected_base_protocol_class = P2PProtocolV4 elif expected_base_protocol_version == DEVP2P_V5: expected_base_protocol_class = P2PProtocolV5 else: raise Exception(f"unrecognized version: {expected_base_protocol_version}") alice_base_protocol = alice_connection.get_base_protocol() bob_base_protocol = bob_connection.get_base_protocol() assert type(alice_base_protocol) is expected_base_protocol_class assert type(bob_base_protocol) is expected_base_protocol_class assert alice_base_protocol.version == expected_base_protocol_version assert bob_base_protocol.version == expected_base_protocol_version assert alice_connection.remote_p2p_version == bob_p2p_version assert bob_connection.remote_p2p_version == alice_p2p_version await do_ping_pong_test(alice_connection, bob_connection)
async def alice_and_bob(): pair_factory = ConnectionPairFactory( alice_client_version='alice', bob_client_version='bob', ) async with pair_factory as (alice, bob): yield alice, bob
async def test_behavior_application(): got_ping = asyncio.Event() class SendPing(ConnectionBehavior): def applies_to(self, connection): return True def __call__(self) -> None: self._connection.get_base_protocol().send_ping() class HasSendPing(Application): name = 'ping-test' def __init__(self): self.send_ping = SendPing() def get_behaviors(self): return (self.send_ping, ) async def handle_ping(connection, msg): got_ping.set() async with ConnectionPairFactory() as (alice, bob): bob.add_command_handler(Ping, handle_ping) # ensure the API isn't already registered assert not alice.has_behavior('ping-test') async with HasSendPing().apply(alice): # ensure it registers with the connect assert alice.has_behavior('ping-test') has_send_ping = alice.get_behavior('ping-test', HasSendPing) has_send_ping.send_ping() await asyncio.wait_for(got_ping.wait(), timeout=2) # ensure it removes itself from the API on exit assert not alice.has_behavior('ping-test')
async def test_connection_safe_client_version_string(): long_client_version_string = 'unicorns\nand\nrainbows\n' * 100 pair_factory = ConnectionPairFactory( bob_client_version=long_client_version_string, ) async with pair_factory as (connection, _): assert connection.client_version_string == long_client_version_string assert len(connection.safe_client_version_string) < len( long_client_version_string) assert '...' in connection.safe_client_version_string
async def test_connection_protocol_and_command_handlers(): alice_handshakers = (NoopHandshaker(SecondProtocol), NoopHandshaker(ThirdProtocol)) bob_handshakers = (NoopHandshaker(SecondProtocol), NoopHandshaker(ThirdProtocol)) pair_factory = ConnectionPairFactory( alice_handshakers=alice_handshakers, bob_handshakers=bob_handshakers, ) async with pair_factory as (alice_connection, bob_connection): messages_cmd_A = [] messages_cmd_D = [] messages_second_protocol = [] done = asyncio.Event() async def _handler_second_protocol(conn, cmd, msg): messages_second_protocol.append((cmd, msg)) async def _handler_cmd_A(conn, msg): messages_cmd_A.append(msg) async def _handler_cmd_D(conn, msg): messages_cmd_D.append(msg) async def _handler_cmd_C(conn, msg): done.set() alice_connection.add_protocol_handler(SecondProtocol, _handler_second_protocol) alice_connection.add_command_handler(CommandA, _handler_cmd_A) alice_connection.add_command_handler(CommandC, _handler_cmd_C) alice_connection.add_command_handler(CommandD, _handler_cmd_D) alice_connection.start_protocol_streams() bob_connection.start_protocol_streams() bob_second_protocol = bob_connection.get_multiplexer( ).get_protocol_by_type(SecondProtocol) bob_third_protocol = bob_connection.get_multiplexer( ).get_protocol_by_type(ThirdProtocol) bob_second_protocol.send_cmd(CommandA) bob_second_protocol.send_cmd(CommandB) bob_third_protocol.send_cmd(CommandD) bob_second_protocol.send_cmd(CommandB) bob_third_protocol.send_cmd(CommandD) bob_second_protocol.send_cmd(CommandA) bob_second_protocol.send_cmd(CommandB) bob_third_protocol.send_cmd(CommandD) bob_third_protocol.send_cmd(CommandC) await asyncio.wait_for(done.wait(), timeout=1) assert len(messages_second_protocol) == 5 assert len(messages_cmd_A) == 2 assert len(messages_cmd_D) == 3
async def test_command_handler_decorator_behavior(): got_ping = asyncio.Event() @command_handler(Ping) async def HandlePing(connection, msg): got_ping.set() async with ConnectionPairFactory() as (alice, bob): ping_handler = HandlePing() async with ping_handler.apply(alice): bob.get_base_protocol().send_ping() await asyncio.wait_for(got_ping.wait(), timeout=2)
async def test_command_handler_logic(): got_ping = asyncio.Event() class HandlePing(CommandHandler): command_type = Ping async def handle(self, connection, msg): got_ping.set() async with ConnectionPairFactory() as (alice, bob): ping_handler = HandlePing() async with ping_handler.as_behavior().apply(alice): bob.get_base_protocol().send(Ping(None)) await asyncio.wait_for(got_ping.wait(), timeout=2)
async def test_behavior_application(): class MyApp(Application): name = 'app-name' qualifier = always async with ConnectionPairFactory() as (alice, bob): # ensure the API isn't already registered assert not alice.has_logic('app-name') async with MyApp().as_behavior().apply(alice): # ensure it registers with the connect assert alice.has_logic('app-name') my_app = alice.get_logic('app-name', MyApp) assert isinstance(my_app, MyApp) # ensure it removes itself from the API on exit assert not alice.has_logic('app-name')
async def test_connection_pair_factory_with_single_protocol(): protocol_class = ProtocolFactory() alice_handshaker = NoopHandshaker(protocol_class) bob_handshaker = NoopHandshaker(protocol_class) pair_factory = ConnectionPairFactory( alice_handshakers=(alice_handshaker,), bob_handshakers=(bob_handshaker,), ) async with pair_factory as (alice_connection, bob_connection): expected_caps = (protocol_class.as_capability(),) assert alice_connection.remote_capabilities == expected_caps assert bob_connection.remote_capabilities == expected_caps await do_ping_pong_test(alice_connection, bob_connection)
async def test_connection_waits_to_feed_protocol_streams(): async with ConnectionPairFactory(start_streams=False) as (alice_connection, bob_connection): got_ping = asyncio.Event() async def _handle_ping(conn, cmd): got_ping.set() alice_connection.add_command_handler(Ping, _handle_ping) bob_base_protocol = bob_connection.get_base_protocol() bob_base_protocol.send(Ping(None)) with pytest.raises(asyncio.TimeoutError): await asyncio.wait_for(got_ping.wait(), timeout=0.1) alice_connection.start_protocol_streams() await asyncio.wait_for(got_ping.wait(), timeout=1)
async def test_connection_behavior_helper(): got_ping = asyncio.Event() class SendPing(ConnectionBehavior): def applies_to(self, connection): return True def __call__(self) -> None: self._connection.get_base_protocol().send_ping() async def handle_ping(connection, msg): got_ping.set() async with ConnectionPairFactory() as (alice, bob): bob.add_command_handler(Ping, handle_ping) send_ping = SendPing() async with send_ping.apply(alice): send_ping() await asyncio.wait_for(got_ping.wait(), timeout=2)
async def test_connection_pair_factory_with_multiple_protocols(): protocol_class_a = ProtocolFactory() protocol_class_b = ProtocolFactory() alice_handshaker_a = NoopHandshaker(protocol_class_a) alice_handshaker_b = NoopHandshaker(protocol_class_b) bob_handshaker_a = NoopHandshaker(protocol_class_a) bob_handshaker_b = NoopHandshaker(protocol_class_b) pair_factory = ConnectionPairFactory( alice_handshakers=(alice_handshaker_a, alice_handshaker_b), bob_handshakers=(bob_handshaker_a, bob_handshaker_b), ) async with pair_factory as (alice_connection, bob_connection): expected_caps = ( protocol_class_a.as_capability(), protocol_class_b.as_capability(), ) assert alice_connection.remote_capabilities == expected_caps assert bob_connection.remote_capabilities == expected_caps await do_ping_pong_test(alice_connection, bob_connection)
async def alice_and_bob(): async with ConnectionPairFactory() as (alice, bob): yield (alice, bob)
async def test_connection_ping_and_pong(): async with ConnectionPairFactory() as (alice_connection, bob_connection): await do_ping_pong_test(alice_connection, bob_connection)
async def test_connection_pair_factory_with_no_protocols(): async with ConnectionPairFactory() as (alice_connection, bob_connection): assert alice_connection.remote_capabilities == () assert bob_connection.remote_capabilities == () await do_ping_pong_test(alice_connection, bob_connection)