mirror of
https://github.com/varun-r-mallya/py-libp2p.git
synced 2026-02-10 15:10:54 +00:00
Add test for host connect and disconnect
This commit is contained in:
2
setup.py
2
setup.py
@ -10,7 +10,7 @@ extras_require = {
|
|||||||
"pytest-asyncio>=0.10.0,<1.0.0",
|
"pytest-asyncio>=0.10.0,<1.0.0",
|
||||||
"pexpect>=4.6,<5",
|
"pexpect>=4.6,<5",
|
||||||
# FIXME: Master branch. Use PyPI instead after it is released.
|
# FIXME: Master branch. Use PyPI instead after it is released.
|
||||||
"p2pclient @ git+https://git@github.com/mhchia/py-libp2p-daemon-bindings@4777c62",
|
"p2pclient @ git+https://git@github.com/mhchia/py-libp2p-daemon-bindings@2647296",
|
||||||
],
|
],
|
||||||
"lint": [
|
"lint": [
|
||||||
"mypy>=0.701,<1.0",
|
"mypy>=0.701,<1.0",
|
||||||
|
|||||||
@ -3,7 +3,8 @@ import asyncio
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from .configs import LISTEN_MADDR
|
from .configs import LISTEN_MADDR
|
||||||
from .factories import HostFactory
|
from .factories import FloodsubFactory, GossipsubFactory, HostFactory, PubsubFactory
|
||||||
|
from .pubsub.configs import GOSSIPSUB_PARAMS
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@ -31,3 +32,50 @@ async def hosts(num_hosts, is_host_secure):
|
|||||||
await asyncio.gather(
|
await asyncio.gather(
|
||||||
*[_host.close() for _host in _hosts], return_exceptions=True
|
*[_host.close() for _host in _hosts], return_exceptions=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def floodsubs(num_hosts):
|
||||||
|
return FloodsubFactory.create_batch(num_hosts)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def gossipsub_params():
|
||||||
|
return GOSSIPSUB_PARAMS
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def gossipsubs(num_hosts, gossipsub_params):
|
||||||
|
yield GossipsubFactory.create_batch(num_hosts, **gossipsub_params._asdict())
|
||||||
|
# TODO: Clean up
|
||||||
|
|
||||||
|
|
||||||
|
def _make_pubsubs(hosts, pubsub_routers, cache_size):
|
||||||
|
if len(pubsub_routers) != len(hosts):
|
||||||
|
raise ValueError(
|
||||||
|
f"lenght of pubsub_routers={pubsub_routers} should be equaled to the "
|
||||||
|
f"length of hosts={len(hosts)}"
|
||||||
|
)
|
||||||
|
return tuple(
|
||||||
|
PubsubFactory(host=host, router=router, cache_size=cache_size)
|
||||||
|
for host, router in zip(hosts, pubsub_routers)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def pubsub_cache_size():
|
||||||
|
return None # default
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def pubsubs_fsub(hosts, floodsubs, pubsub_cache_size):
|
||||||
|
_pubsubs_fsub = _make_pubsubs(hosts, floodsubs, pubsub_cache_size)
|
||||||
|
yield _pubsubs_fsub
|
||||||
|
# TODO: Clean up
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def pubsubs_gsub(hosts, gossipsubs, pubsub_cache_size):
|
||||||
|
_pubsubs_gsub = _make_pubsubs(hosts, gossipsubs, pubsub_cache_size)
|
||||||
|
yield _pubsubs_gsub
|
||||||
|
# TODO: Clean up
|
||||||
|
|||||||
@ -1,8 +1,11 @@
|
|||||||
|
import asyncio
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import pexpect
|
import pexpect
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from .daemon import make_p2pd
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def proc_factory():
|
def proc_factory():
|
||||||
@ -22,3 +25,19 @@ def proc_factory():
|
|||||||
finally:
|
finally:
|
||||||
for proc in procs:
|
for proc in procs:
|
||||||
proc.close()
|
proc.close()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def num_p2pds():
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
async def p2pds(num_p2pds, is_host_secure, unused_tcp_port_factory):
|
||||||
|
p2pds = await asyncio.gather(
|
||||||
|
*[make_p2pd(unused_tcp_port_factory, is_host_secure) for _ in range(num_p2pds)]
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
yield p2pds
|
||||||
|
finally:
|
||||||
|
await asyncio.gather(*[p2pd.close() for p2pd in p2pds])
|
||||||
|
|||||||
@ -39,14 +39,16 @@ class P2PDProcess:
|
|||||||
cmd: str = str(P2PD_PATH)
|
cmd: str = str(P2PD_PATH)
|
||||||
args: List[Any]
|
args: List[Any]
|
||||||
|
|
||||||
|
_tasks: List["asyncio.Future[Any]"]
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
control_maddr: Multiaddr,
|
control_maddr: Multiaddr,
|
||||||
is_secure: bool,
|
is_secure: bool,
|
||||||
is_pubsub_enabled=True,
|
is_pubsub_enabled: bool = True,
|
||||||
is_gossipsub=True,
|
is_gossipsub: bool = True,
|
||||||
is_pubsub_signing=False,
|
is_pubsub_signing: bool = False,
|
||||||
is_pubsub_signing_strict=False,
|
is_pubsub_signing_strict: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
args = [f"-listen={str(control_maddr)}"]
|
args = [f"-listen={str(control_maddr)}"]
|
||||||
# NOTE: To support `-insecure`, we need to hack `go-libp2p-daemon`.
|
# NOTE: To support `-insecure`, we need to hack `go-libp2p-daemon`.
|
||||||
@ -68,6 +70,7 @@ class P2PDProcess:
|
|||||||
# - gossipsubHeartbeatInitialDelay: GossipSubHeartbeatInterval = 1 * time.Second
|
# - gossipsubHeartbeatInitialDelay: GossipSubHeartbeatInterval = 1 * time.Second
|
||||||
# Referece: https://github.com/libp2p/go-libp2p-daemon/blob/b95e77dbfcd186ccf817f51e95f73f9fd5982600/p2pd/main.go#L348-L353 # noqa: E501
|
# Referece: https://github.com/libp2p/go-libp2p-daemon/blob/b95e77dbfcd186ccf817f51e95f73f9fd5982600/p2pd/main.go#L348-L353 # noqa: E501
|
||||||
self.args = args
|
self.args = args
|
||||||
|
self._tasks = []
|
||||||
|
|
||||||
async def wait_until_ready(self):
|
async def wait_until_ready(self):
|
||||||
lines_head_pattern = (b"Control socket:", b"Peer ID:", b"Peer Addrs:")
|
lines_head_pattern = (b"Control socket:", b"Peer ID:", b"Peer Addrs:")
|
||||||
@ -84,6 +87,24 @@ class P2PDProcess:
|
|||||||
# Sleep a little bit to ensure the listener is up after logs are emitted.
|
# Sleep a little bit to ensure the listener is up after logs are emitted.
|
||||||
await asyncio.sleep(0.01)
|
await asyncio.sleep(0.01)
|
||||||
|
|
||||||
|
async def start_printing_logs(self) -> None:
|
||||||
|
async def _print_from_stream(
|
||||||
|
src_name: str, reader: asyncio.StreamReader
|
||||||
|
) -> None:
|
||||||
|
while True:
|
||||||
|
line = await reader.readline()
|
||||||
|
if line != b"":
|
||||||
|
print(f"{src_name}\t: {line.rstrip().decode()}")
|
||||||
|
await asyncio.sleep(0.01)
|
||||||
|
|
||||||
|
self._tasks.append(
|
||||||
|
asyncio.ensure_future(_print_from_stream("out", self.proc.stdout))
|
||||||
|
)
|
||||||
|
self._tasks.append(
|
||||||
|
asyncio.ensure_future(_print_from_stream("err", self.proc.stderr))
|
||||||
|
)
|
||||||
|
await asyncio.sleep(0)
|
||||||
|
|
||||||
async def start(self) -> None:
|
async def start(self) -> None:
|
||||||
self.proc = await asyncio.subprocess.create_subprocess_exec(
|
self.proc = await asyncio.subprocess.create_subprocess_exec(
|
||||||
self.cmd,
|
self.cmd,
|
||||||
@ -93,10 +114,13 @@ class P2PDProcess:
|
|||||||
bufsize=0,
|
bufsize=0,
|
||||||
)
|
)
|
||||||
await self.wait_until_ready()
|
await self.wait_until_ready()
|
||||||
|
await self.start_printing_logs()
|
||||||
|
|
||||||
async def close(self) -> None:
|
async def close(self) -> None:
|
||||||
self.proc.terminate()
|
self.proc.terminate()
|
||||||
await self.proc.wait()
|
await self.proc.wait()
|
||||||
|
for task in self._tasks:
|
||||||
|
task.cancel()
|
||||||
|
|
||||||
|
|
||||||
class Daemon:
|
class Daemon:
|
||||||
@ -165,5 +189,4 @@ async def make_p2pd(
|
|||||||
peer_info = info_from_p2p_addr(
|
peer_info = info_from_p2p_addr(
|
||||||
listen_maddr.encapsulate(Multiaddr(f"/p2p/{peer_id.to_string()}"))
|
listen_maddr.encapsulate(Multiaddr(f"/p2p/{peer_id.to_string()}"))
|
||||||
)
|
)
|
||||||
print(f"!@# peer_info: peer_id={peer_info.peer_id}, maddrs={peer_info.addrs}")
|
|
||||||
return Daemon(p2pd_proc, p2pc, peer_info)
|
return Daemon(p2pd_proc, p2pc, peer_info)
|
||||||
|
|||||||
29
tests/interop/test_bindings.py
Normal file
29
tests/interop/test_bindings.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import asyncio
|
||||||
|
|
||||||
|
from multiaddr import Multiaddr
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("num_hosts", (1,))
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_connect(hosts, p2pds):
|
||||||
|
p2pd = p2pds[0]
|
||||||
|
host = hosts[0]
|
||||||
|
assert len(await p2pd.control.list_peers()) == 0
|
||||||
|
# Test: connect from Py
|
||||||
|
await host.connect(p2pd.peer_info)
|
||||||
|
assert len(await p2pd.control.list_peers()) == 1
|
||||||
|
# Test: `disconnect` from Py
|
||||||
|
await host.disconnect(p2pd.peer_id)
|
||||||
|
assert len(await p2pd.control.list_peers()) == 0
|
||||||
|
# Test: connect from Go
|
||||||
|
py_peer_id = host.get_id()
|
||||||
|
await p2pd.control.connect(
|
||||||
|
host.get_id(),
|
||||||
|
[host.get_addrs()[0].decapsulate(Multiaddr(f"/p2p/{py_peer_id.to_string()}"))],
|
||||||
|
)
|
||||||
|
assert len(host.get_network().connections) == 1
|
||||||
|
# Test: `disconnect` from Go
|
||||||
|
await p2pd.control.disconnect(py_peer_id)
|
||||||
|
# FIXME: Failed to handle disconnect
|
||||||
|
# assert len(host.get_network().connections) == 0
|
||||||
@ -1,18 +0,0 @@
|
|||||||
import pytest
|
|
||||||
|
|
||||||
from .daemon import make_p2pd
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("num_hosts", (1,))
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_pubsub_init(hosts, is_host_secure, unused_tcp_port_factory):
|
|
||||||
try:
|
|
||||||
p2pd = await make_p2pd(unused_tcp_port_factory, is_host_secure)
|
|
||||||
host = hosts[0]
|
|
||||||
peers = await p2pd.control.list_peers()
|
|
||||||
assert len(peers) == 0
|
|
||||||
await host.connect(p2pd.peer_info)
|
|
||||||
peers = await p2pd.control.list_peers()
|
|
||||||
assert len(peers) != 0
|
|
||||||
finally:
|
|
||||||
await p2pd.close()
|
|
||||||
@ -1,51 +0,0 @@
|
|||||||
import pytest
|
|
||||||
|
|
||||||
from tests.factories import FloodsubFactory, GossipsubFactory, PubsubFactory
|
|
||||||
from tests.pubsub.configs import GOSSIPSUB_PARAMS
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def floodsubs(num_hosts):
|
|
||||||
return FloodsubFactory.create_batch(num_hosts)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def gossipsub_params():
|
|
||||||
return GOSSIPSUB_PARAMS
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def gossipsubs(num_hosts, gossipsub_params):
|
|
||||||
yield GossipsubFactory.create_batch(num_hosts, **gossipsub_params._asdict())
|
|
||||||
# TODO: Clean up
|
|
||||||
|
|
||||||
|
|
||||||
def _make_pubsubs(hosts, pubsub_routers, cache_size):
|
|
||||||
if len(pubsub_routers) != len(hosts):
|
|
||||||
raise ValueError(
|
|
||||||
f"lenght of pubsub_routers={pubsub_routers} should be equaled to the "
|
|
||||||
f"length of hosts={len(hosts)}"
|
|
||||||
)
|
|
||||||
return tuple(
|
|
||||||
PubsubFactory(host=host, router=router, cache_size=cache_size)
|
|
||||||
for host, router in zip(hosts, pubsub_routers)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def pubsub_cache_size():
|
|
||||||
return None # default
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def pubsubs_fsub(hosts, floodsubs, pubsub_cache_size):
|
|
||||||
_pubsubs_fsub = _make_pubsubs(hosts, floodsubs, pubsub_cache_size)
|
|
||||||
yield _pubsubs_fsub
|
|
||||||
# TODO: Clean up
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def pubsubs_gsub(hosts, gossipsubs, pubsub_cache_size):
|
|
||||||
_pubsubs_gsub = _make_pubsubs(hosts, gossipsubs, pubsub_cache_size)
|
|
||||||
yield _pubsubs_gsub
|
|
||||||
# TODO: Clean up
|
|
||||||
|
|||||||
Reference in New Issue
Block a user