Skip to content

Commit 8afb99c

Browse files
committed
add test for counded concurrency
Signed-off-by: sukhman <[email protected]>
1 parent ae16909 commit 8afb99c

File tree

2 files changed

+93
-9
lines changed

2 files changed

+93
-9
lines changed

tests/core/identity/identify_push/test_identify_push.py

Lines changed: 82 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import logging
2+
from unittest.mock import (
3+
patch,
4+
)
25

36
import pytest
47
import multiaddr
@@ -29,10 +32,20 @@
2932
from tests.utils.factories import (
3033
host_pair_factory,
3134
)
35+
from tests.utils.utils import (
36+
create_mock_connections,
37+
)
3238

3339
logger = logging.getLogger("libp2p.identity.identify-push-test")
3440

3541

42+
CONCURRENCY_LIMIT = 10
43+
LIMIT = trio.Semaphore(CONCURRENCY_LIMIT)
44+
concurrency_counter = 0
45+
max_observed = 0
46+
lock = trio.Lock()
47+
48+
3649
@pytest.mark.trio
3750
async def test_identify_push_protocol(security_protocol):
3851
"""
@@ -204,20 +217,20 @@ async def test_identify_push_to_peers(security_protocol):
204217

205218
# Check that the peer is in the peerstore
206219
assert peer_id_a in peerstore_c.peer_ids()
207-
208-
# Test for push_identify to only connected peers and not all peers in peerstore
220+
221+
# Test for push_identify to only connected peers and not all peers
209222
# Disconnect a from c.
210223
await host_c.disconnect(host_a.get_id())
211-
#
224+
#
212225
await push_identify_to_peers(host_c)
213-
226+
214227
# Wait a bit for the push to complete
215228
await trio.sleep(0.1)
216-
217-
# Check that host_a's peerstore has not been updated with host_c's information
218-
assert(host_c.get_id() not in host_a.get_peerstore().peer_ids())
219-
# Check that host_b's peerstore has been updated with host_c's information
220-
assert(host_c.get_id() in host_b.get_peerstore().peer_ids())
229+
230+
# Check that host_a's peerstore has not been updated with host_c's info
231+
assert host_c.get_id() not in host_a.get_peerstore().peer_ids()
232+
# Check that host_b's peerstore has been updated with host_c's info
233+
assert host_c.get_id() in host_b.get_peerstore().peer_ids()
221234

222235

223236
@pytest.mark.trio
@@ -427,3 +440,63 @@ async def test_partial_update_peerstore_from_identify(security_protocol):
427440
host_a_public_key = host_a.get_public_key().serialize()
428441
peerstore_public_key = peerstore.pubkey(peer_id).serialize()
429442
assert host_a_public_key == peerstore_public_key
443+
444+
445+
@pytest.mark.trio
446+
async def test_push_identify_to_peers_respects_concurrency_limit():
447+
"""
448+
Test bounded concurrency for the identify/push protocol to prevent
449+
network congestion.
450+
451+
This test verifies:
452+
1. The number of concurrent tasks executing the identify push is always
453+
less than or equal to CONCURRENCY_LIMIT.
454+
2. An error is raised if concurrency exceeds the defined limit.
455+
456+
It mocks `push_identify_to_peer` to simulate delay using sleep,
457+
allowing the test to measure and assert actual concurrency behavior.
458+
"""
459+
460+
# Create a mock host.
461+
key_pair_host = create_new_key_pair()
462+
host = new_host(key_pair=key_pair_host)
463+
464+
# Create a mock network and add mock connections to the host
465+
host.get_network().connections = create_mock_connections()
466+
with patch(
467+
"libp2p.identity.identify_push.identify_push.push_identify_to_peer",
468+
new=mock_push_identify_to_peer,
469+
):
470+
await push_identify_to_peers(host)
471+
assert (
472+
max_observed <= CONCURRENCY_LIMIT
473+
), f"Max concurrency observed: {max_observed}"
474+
475+
476+
async def mock_push_identify_to_peer(host, peer_id, observed_multiaddr=None) -> bool:
477+
"""
478+
Mock function to test concurrency by simulating an identify message.
479+
480+
This function patches push_identify_to_peer for testing purpose
481+
482+
Returns
483+
-------
484+
bool
485+
True if the push was successful, False otherwise.
486+
"""
487+
global concurrency_counter, max_observed
488+
489+
async with LIMIT:
490+
async with lock:
491+
concurrency_counter += 1
492+
if concurrency_counter > CONCURRENCY_LIMIT:
493+
raise RuntimeError(f"Concurrency limit exceeded: {concurrency_counter}")
494+
max_observed = max(max_observed, concurrency_counter)
495+
496+
logger.debug("Successfully pushed identify to peer %s", peer_id)
497+
await trio.sleep(0.05)
498+
499+
async with lock:
500+
concurrency_counter -= 1
501+
502+
return True

tests/utils/utils.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from unittest.mock import MagicMock
2+
3+
def create_mock_connections() -> dict:
4+
connections = {}
5+
6+
for i in range(1, 31):
7+
peer_id = f"peer-{i}"
8+
mock_conn = MagicMock(name=f"INetConn-{i}")
9+
connections[peer_id] = mock_conn
10+
11+
return connections

0 commit comments

Comments
 (0)