mirror of
https://github.com/varun-r-mallya/py-libp2p.git
synced 2025-12-31 20:36:24 +00:00
reorg test structure to match tox and CI jobs, drop bumpversion for bump-my-version and move config to pyproject.toml, fix docs building
This commit is contained in:
29
tests/core/network/conftest.py
Normal file
29
tests/core/network/conftest.py
Normal file
@ -0,0 +1,29 @@
|
||||
import pytest
|
||||
|
||||
from libp2p.tools.factories import (
|
||||
net_stream_pair_factory,
|
||||
swarm_conn_pair_factory,
|
||||
swarm_pair_factory,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def net_stream_pair(security_protocol):
|
||||
async with net_stream_pair_factory(
|
||||
security_protocol=security_protocol
|
||||
) as net_stream_pair:
|
||||
yield net_stream_pair
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def swarm_pair(security_protocol):
|
||||
async with swarm_pair_factory(security_protocol=security_protocol) as swarms:
|
||||
yield swarms
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def swarm_conn_pair(security_protocol):
|
||||
async with swarm_conn_pair_factory(
|
||||
security_protocol=security_protocol
|
||||
) as swarm_conn_pair:
|
||||
yield swarm_conn_pair
|
||||
120
tests/core/network/test_net_stream.py
Normal file
120
tests/core/network/test_net_stream.py
Normal file
@ -0,0 +1,120 @@
|
||||
import pytest
|
||||
import trio
|
||||
|
||||
from libp2p.network.stream.exceptions import (
|
||||
StreamClosed,
|
||||
StreamEOF,
|
||||
StreamReset,
|
||||
)
|
||||
from libp2p.tools.constants import (
|
||||
MAX_READ_LEN,
|
||||
)
|
||||
|
||||
DATA = b"data_123"
|
||||
|
||||
|
||||
@pytest.mark.trio
|
||||
async def test_net_stream_read_write(net_stream_pair):
|
||||
stream_0, stream_1 = net_stream_pair
|
||||
assert (
|
||||
stream_0.protocol_id is not None
|
||||
and stream_0.protocol_id == stream_1.protocol_id
|
||||
)
|
||||
await stream_0.write(DATA)
|
||||
assert (await stream_1.read(MAX_READ_LEN)) == DATA
|
||||
|
||||
|
||||
@pytest.mark.trio
|
||||
async def test_net_stream_read_until_eof(net_stream_pair):
|
||||
read_bytes = bytearray()
|
||||
stream_0, stream_1 = net_stream_pair
|
||||
|
||||
async def read_until_eof():
|
||||
read_bytes.extend(await stream_1.read())
|
||||
|
||||
async with trio.open_nursery() as nursery:
|
||||
nursery.start_soon(read_until_eof)
|
||||
expected_data = bytearray()
|
||||
|
||||
# Test: `read` doesn't return before `close` is called.
|
||||
await stream_0.write(DATA)
|
||||
expected_data.extend(DATA)
|
||||
await trio.sleep(0.01)
|
||||
assert len(read_bytes) == 0
|
||||
# Test: `read` doesn't return before `close` is called.
|
||||
await stream_0.write(DATA)
|
||||
expected_data.extend(DATA)
|
||||
await trio.sleep(0.01)
|
||||
assert len(read_bytes) == 0
|
||||
|
||||
# Test: Close the stream, `read` returns, and receive previous sent data.
|
||||
await stream_0.close()
|
||||
await trio.sleep(0.01)
|
||||
assert read_bytes == expected_data
|
||||
|
||||
|
||||
@pytest.mark.trio
|
||||
async def test_net_stream_read_after_remote_closed(net_stream_pair):
|
||||
stream_0, stream_1 = net_stream_pair
|
||||
await stream_0.write(DATA)
|
||||
await stream_0.close()
|
||||
await trio.sleep(0.01)
|
||||
assert (await stream_1.read(MAX_READ_LEN)) == DATA
|
||||
with pytest.raises(StreamEOF):
|
||||
await stream_1.read(MAX_READ_LEN)
|
||||
|
||||
|
||||
@pytest.mark.trio
|
||||
async def test_net_stream_read_after_local_reset(net_stream_pair):
|
||||
stream_0, stream_1 = net_stream_pair
|
||||
await stream_0.reset()
|
||||
with pytest.raises(StreamReset):
|
||||
await stream_0.read(MAX_READ_LEN)
|
||||
|
||||
|
||||
@pytest.mark.trio
|
||||
async def test_net_stream_read_after_remote_reset(net_stream_pair):
|
||||
stream_0, stream_1 = net_stream_pair
|
||||
await stream_0.write(DATA)
|
||||
await stream_0.reset()
|
||||
# Sleep to let `stream_1` receive the message.
|
||||
await trio.sleep(0.01)
|
||||
with pytest.raises(StreamReset):
|
||||
await stream_1.read(MAX_READ_LEN)
|
||||
|
||||
|
||||
@pytest.mark.trio
|
||||
async def test_net_stream_read_after_remote_closed_and_reset(net_stream_pair):
|
||||
stream_0, stream_1 = net_stream_pair
|
||||
await stream_0.write(DATA)
|
||||
await stream_0.close()
|
||||
await stream_0.reset()
|
||||
# Sleep to let `stream_1` receive the message.
|
||||
await trio.sleep(0.01)
|
||||
assert (await stream_1.read(MAX_READ_LEN)) == DATA
|
||||
|
||||
|
||||
@pytest.mark.trio
|
||||
async def test_net_stream_write_after_local_closed(net_stream_pair):
|
||||
stream_0, stream_1 = net_stream_pair
|
||||
await stream_0.write(DATA)
|
||||
await stream_0.close()
|
||||
with pytest.raises(StreamClosed):
|
||||
await stream_0.write(DATA)
|
||||
|
||||
|
||||
@pytest.mark.trio
|
||||
async def test_net_stream_write_after_local_reset(net_stream_pair):
|
||||
stream_0, stream_1 = net_stream_pair
|
||||
await stream_0.reset()
|
||||
with pytest.raises(StreamClosed):
|
||||
await stream_0.write(DATA)
|
||||
|
||||
|
||||
@pytest.mark.trio
|
||||
async def test_net_stream_write_after_remote_reset(net_stream_pair):
|
||||
stream_0, stream_1 = net_stream_pair
|
||||
await stream_1.reset()
|
||||
await trio.sleep(0.01)
|
||||
with pytest.raises(StreamClosed):
|
||||
await stream_0.write(DATA)
|
||||
126
tests/core/network/test_notify.py
Normal file
126
tests/core/network/test_notify.py
Normal file
@ -0,0 +1,126 @@
|
||||
"""
|
||||
Test Notify and Notifee by ensuring that the proper events get called, and that
|
||||
the stream passed into opened_stream is correct.
|
||||
|
||||
Note: Listen event does not get hit because MyNotifee is passed
|
||||
into network after network has already started listening
|
||||
|
||||
TODO: Add tests for closed_stream, listen_close when those
|
||||
features are implemented in swarm
|
||||
"""
|
||||
import enum
|
||||
|
||||
from async_service import (
|
||||
background_trio_service,
|
||||
)
|
||||
import pytest
|
||||
import trio
|
||||
|
||||
from libp2p.network.notifee_interface import (
|
||||
INotifee,
|
||||
)
|
||||
from libp2p.tools.constants import (
|
||||
LISTEN_MADDR,
|
||||
)
|
||||
from libp2p.tools.factories import (
|
||||
SwarmFactory,
|
||||
)
|
||||
from libp2p.tools.utils import (
|
||||
connect_swarm,
|
||||
)
|
||||
|
||||
|
||||
class Event(enum.Enum):
|
||||
OpenedStream = 0
|
||||
ClosedStream = 1 # Not implemented
|
||||
Connected = 2
|
||||
Disconnected = 3
|
||||
Listen = 4
|
||||
ListenClose = 5 # Not implemented
|
||||
|
||||
|
||||
class MyNotifee(INotifee):
|
||||
def __init__(self, events):
|
||||
self.events = events
|
||||
|
||||
async def opened_stream(self, network, stream):
|
||||
self.events.append(Event.OpenedStream)
|
||||
|
||||
async def closed_stream(self, network, stream):
|
||||
# TODO: It is not implemented yet.
|
||||
pass
|
||||
|
||||
async def connected(self, network, conn):
|
||||
self.events.append(Event.Connected)
|
||||
|
||||
async def disconnected(self, network, conn):
|
||||
self.events.append(Event.Disconnected)
|
||||
|
||||
async def listen(self, network, _multiaddr):
|
||||
self.events.append(Event.Listen)
|
||||
|
||||
async def listen_close(self, network, _multiaddr):
|
||||
# TODO: It is not implemented yet.
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.trio
|
||||
async def test_notify(security_protocol):
|
||||
swarms = [SwarmFactory(security_protocol=security_protocol) for _ in range(2)]
|
||||
|
||||
events_0_0 = []
|
||||
events_1_0 = []
|
||||
events_0_without_listen = []
|
||||
# Run swarms.
|
||||
async with background_trio_service(swarms[0]), background_trio_service(swarms[1]):
|
||||
# Register events before listening, to allow `MyNotifee` is notified with the
|
||||
# event `listen`.
|
||||
swarms[0].register_notifee(MyNotifee(events_0_0))
|
||||
swarms[1].register_notifee(MyNotifee(events_1_0))
|
||||
|
||||
# Listen
|
||||
async with trio.open_nursery() as nursery:
|
||||
nursery.start_soon(swarms[0].listen, LISTEN_MADDR)
|
||||
nursery.start_soon(swarms[1].listen, LISTEN_MADDR)
|
||||
|
||||
swarms[0].register_notifee(MyNotifee(events_0_without_listen))
|
||||
|
||||
# Connected
|
||||
await connect_swarm(swarms[0], swarms[1])
|
||||
# OpenedStream: first
|
||||
await swarms[0].new_stream(swarms[1].get_peer_id())
|
||||
# OpenedStream: second
|
||||
await swarms[0].new_stream(swarms[1].get_peer_id())
|
||||
# OpenedStream: third, but different direction.
|
||||
await swarms[1].new_stream(swarms[0].get_peer_id())
|
||||
|
||||
await trio.sleep(0.01)
|
||||
|
||||
# TODO: Check `ClosedStream` and `ListenClose` events after they are ready.
|
||||
|
||||
# Disconnected
|
||||
await swarms[0].close_peer(swarms[1].get_peer_id())
|
||||
await trio.sleep(0.01)
|
||||
|
||||
# Connected again, but different direction.
|
||||
await connect_swarm(swarms[1], swarms[0])
|
||||
await trio.sleep(0.01)
|
||||
|
||||
# Disconnected again, but different direction.
|
||||
await swarms[1].close_peer(swarms[0].get_peer_id())
|
||||
await trio.sleep(0.01)
|
||||
|
||||
expected_events_without_listen = [
|
||||
Event.Connected,
|
||||
Event.OpenedStream,
|
||||
Event.OpenedStream,
|
||||
Event.OpenedStream,
|
||||
Event.Disconnected,
|
||||
Event.Connected,
|
||||
Event.Disconnected,
|
||||
]
|
||||
expected_events = [Event.Listen] + expected_events_without_listen
|
||||
|
||||
assert events_0_0 == expected_events
|
||||
assert events_1_0 == expected_events
|
||||
assert events_0_without_listen == expected_events_without_listen
|
||||
158
tests/core/network/test_swarm.py
Normal file
158
tests/core/network/test_swarm.py
Normal file
@ -0,0 +1,158 @@
|
||||
from multiaddr import (
|
||||
Multiaddr,
|
||||
)
|
||||
import pytest
|
||||
import trio
|
||||
from trio.testing import (
|
||||
wait_all_tasks_blocked,
|
||||
)
|
||||
|
||||
from libp2p.network.exceptions import (
|
||||
SwarmException,
|
||||
)
|
||||
from libp2p.tools.factories import (
|
||||
SwarmFactory,
|
||||
)
|
||||
from libp2p.tools.utils import (
|
||||
connect_swarm,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.trio
|
||||
async def test_swarm_dial_peer(security_protocol):
|
||||
async with SwarmFactory.create_batch_and_listen(
|
||||
3, security_protocol=security_protocol
|
||||
) as swarms:
|
||||
# Test: No addr found.
|
||||
with pytest.raises(SwarmException):
|
||||
await swarms[0].dial_peer(swarms[1].get_peer_id())
|
||||
|
||||
# Test: len(addr) in the peerstore is 0.
|
||||
swarms[0].peerstore.add_addrs(swarms[1].get_peer_id(), [], 10000)
|
||||
with pytest.raises(SwarmException):
|
||||
await swarms[0].dial_peer(swarms[1].get_peer_id())
|
||||
|
||||
# Test: Succeed if addrs of the peer_id are present in the peerstore.
|
||||
addrs = tuple(
|
||||
addr
|
||||
for transport in swarms[1].listeners.values()
|
||||
for addr in transport.get_addrs()
|
||||
)
|
||||
swarms[0].peerstore.add_addrs(swarms[1].get_peer_id(), addrs, 10000)
|
||||
await swarms[0].dial_peer(swarms[1].get_peer_id())
|
||||
assert swarms[0].get_peer_id() in swarms[1].connections
|
||||
assert swarms[1].get_peer_id() in swarms[0].connections
|
||||
|
||||
# Test: Reuse connections when we already have ones with a peer.
|
||||
conn_to_1 = swarms[0].connections[swarms[1].get_peer_id()]
|
||||
conn = await swarms[0].dial_peer(swarms[1].get_peer_id())
|
||||
assert conn is conn_to_1
|
||||
|
||||
|
||||
@pytest.mark.trio
|
||||
async def test_swarm_close_peer(security_protocol):
|
||||
async with SwarmFactory.create_batch_and_listen(
|
||||
3, security_protocol=security_protocol
|
||||
) as swarms:
|
||||
# 0 <> 1 <> 2
|
||||
await connect_swarm(swarms[0], swarms[1])
|
||||
await connect_swarm(swarms[1], swarms[2])
|
||||
|
||||
# peer 1 closes peer 0
|
||||
await swarms[1].close_peer(swarms[0].get_peer_id())
|
||||
await trio.sleep(0.01)
|
||||
await wait_all_tasks_blocked()
|
||||
# 0 1 <> 2
|
||||
assert len(swarms[0].connections) == 0
|
||||
assert (
|
||||
len(swarms[1].connections) == 1
|
||||
and swarms[2].get_peer_id() in swarms[1].connections
|
||||
)
|
||||
|
||||
# peer 1 is closed by peer 2
|
||||
await swarms[2].close_peer(swarms[1].get_peer_id())
|
||||
await trio.sleep(0.01)
|
||||
# 0 1 2
|
||||
assert len(swarms[1].connections) == 0 and len(swarms[2].connections) == 0
|
||||
|
||||
await connect_swarm(swarms[0], swarms[1])
|
||||
# 0 <> 1 2
|
||||
assert (
|
||||
len(swarms[0].connections) == 1
|
||||
and swarms[1].get_peer_id() in swarms[0].connections
|
||||
)
|
||||
assert (
|
||||
len(swarms[1].connections) == 1
|
||||
and swarms[0].get_peer_id() in swarms[1].connections
|
||||
)
|
||||
# peer 0 closes peer 1
|
||||
await swarms[0].close_peer(swarms[1].get_peer_id())
|
||||
await trio.sleep(0.01)
|
||||
# 0 1 2
|
||||
assert len(swarms[1].connections) == 0 and len(swarms[2].connections) == 0
|
||||
|
||||
|
||||
@pytest.mark.trio
|
||||
async def test_swarm_remove_conn(swarm_pair):
|
||||
swarm_0, swarm_1 = swarm_pair
|
||||
conn_0 = swarm_0.connections[swarm_1.get_peer_id()]
|
||||
swarm_0.remove_conn(conn_0)
|
||||
assert swarm_1.get_peer_id() not in swarm_0.connections
|
||||
# Test: Remove twice. There should not be errors.
|
||||
swarm_0.remove_conn(conn_0)
|
||||
assert swarm_1.get_peer_id() not in swarm_0.connections
|
||||
|
||||
|
||||
@pytest.mark.trio
|
||||
async def test_swarm_multiaddr(security_protocol):
|
||||
async with SwarmFactory.create_batch_and_listen(
|
||||
3, security_protocol=security_protocol
|
||||
) as swarms:
|
||||
|
||||
def clear():
|
||||
swarms[0].peerstore.clear_addrs(swarms[1].get_peer_id())
|
||||
|
||||
clear()
|
||||
# No addresses
|
||||
with pytest.raises(SwarmException):
|
||||
await swarms[0].dial_peer(swarms[1].get_peer_id())
|
||||
|
||||
clear()
|
||||
# Wrong addresses
|
||||
swarms[0].peerstore.add_addrs(
|
||||
swarms[1].get_peer_id(), [Multiaddr("/ip4/0.0.0.0/tcp/9999")], 10000
|
||||
)
|
||||
|
||||
with pytest.raises(SwarmException):
|
||||
await swarms[0].dial_peer(swarms[1].get_peer_id())
|
||||
|
||||
clear()
|
||||
# Multiple wrong addresses
|
||||
swarms[0].peerstore.add_addrs(
|
||||
swarms[1].get_peer_id(),
|
||||
[Multiaddr("/ip4/0.0.0.0/tcp/9999"), Multiaddr("/ip4/0.0.0.0/tcp/9998")],
|
||||
10000,
|
||||
)
|
||||
|
||||
with pytest.raises(SwarmException):
|
||||
await swarms[0].dial_peer(swarms[1].get_peer_id())
|
||||
|
||||
# Test one address
|
||||
addrs = tuple(
|
||||
addr
|
||||
for transport in swarms[1].listeners.values()
|
||||
for addr in transport.get_addrs()
|
||||
)
|
||||
|
||||
swarms[0].peerstore.add_addrs(swarms[1].get_peer_id(), addrs[:1], 10000)
|
||||
await swarms[0].dial_peer(swarms[1].get_peer_id())
|
||||
|
||||
# Test multiple addresses
|
||||
addrs = tuple(
|
||||
addr
|
||||
for transport in swarms[1].listeners.values()
|
||||
for addr in transport.get_addrs()
|
||||
)
|
||||
|
||||
swarms[0].peerstore.add_addrs(swarms[1].get_peer_id(), addrs + addrs, 10000)
|
||||
await swarms[0].dial_peer(swarms[1].get_peer_id())
|
||||
48
tests/core/network/test_swarm_conn.py
Normal file
48
tests/core/network/test_swarm_conn.py
Normal file
@ -0,0 +1,48 @@
|
||||
import pytest
|
||||
import trio
|
||||
from trio.testing import (
|
||||
wait_all_tasks_blocked,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.trio
|
||||
async def test_swarm_conn_close(swarm_conn_pair):
|
||||
conn_0, conn_1 = swarm_conn_pair
|
||||
|
||||
assert not conn_0.is_closed
|
||||
assert not conn_1.is_closed
|
||||
|
||||
await conn_0.close()
|
||||
|
||||
await trio.sleep(0.1)
|
||||
await wait_all_tasks_blocked()
|
||||
|
||||
assert conn_0.is_closed
|
||||
assert conn_1.is_closed
|
||||
assert conn_0 not in conn_0.swarm.connections.values()
|
||||
assert conn_1 not in conn_1.swarm.connections.values()
|
||||
|
||||
|
||||
@pytest.mark.trio
|
||||
async def test_swarm_conn_streams(swarm_conn_pair):
|
||||
conn_0, conn_1 = swarm_conn_pair
|
||||
|
||||
assert len(conn_0.get_streams()) == 0
|
||||
assert len(conn_1.get_streams()) == 0
|
||||
|
||||
stream_0_0 = await conn_0.new_stream()
|
||||
await trio.sleep(0.01)
|
||||
assert len(conn_0.get_streams()) == 1
|
||||
assert len(conn_1.get_streams()) == 1
|
||||
|
||||
stream_0_1 = await conn_0.new_stream()
|
||||
await trio.sleep(0.01)
|
||||
assert len(conn_0.get_streams()) == 2
|
||||
assert len(conn_1.get_streams()) == 2
|
||||
|
||||
conn_0.remove_stream(stream_0_0)
|
||||
assert len(conn_0.get_streams()) == 1
|
||||
conn_0.remove_stream(stream_0_1)
|
||||
assert len(conn_0.get_streams()) == 0
|
||||
# Nothing happen if `stream_0_1` is not present or already removed.
|
||||
conn_0.remove_stream(stream_0_1)
|
||||
Reference in New Issue
Block a user