You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
132 lines
4.3 KiB
132 lines
4.3 KiB
"""Proxy use cases and packet helpers (Phase 3)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import time
|
|
|
|
import pytest
|
|
|
|
from adn_server.application.proxy import ProxyUseCases, peer_id_from_packet
|
|
from adn_server.domain.proxy import ClientEndpoint
|
|
from adn_server.domain.result import is_fail, is_ok
|
|
from adn_server.domain.value_objects import bytes_4
|
|
from adn_server.infrastructure.proxy import InMemoryPendingRptoQueue, InMemoryProxySlotStore
|
|
|
|
_PEER_A = bytes_4(1234567)
|
|
_PEER_B = bytes_4(7654321)
|
|
_MAX_PEERS = 4
|
|
|
|
|
|
def _service(*, max_peers: int = _MAX_PEERS, black_list: tuple[int, ...] = ()) -> ProxyUseCases:
|
|
return ProxyUseCases(
|
|
InMemoryProxySlotStore(),
|
|
InMemoryPendingRptoQueue(),
|
|
max_peers=max_peers,
|
|
black_list=black_list,
|
|
)
|
|
|
|
|
|
def test_attach_creates_session_and_refreshes_client() -> None:
|
|
svc = _service()
|
|
first = svc.attach_client(_PEER_A, "10.0.0.1", 62031)
|
|
assert is_ok(first)
|
|
slot = first.value
|
|
assert slot.client == ClientEndpoint(host="10.0.0.1", port=62031)
|
|
|
|
again = svc.attach_client(_PEER_A, "10.0.0.2", 62032)
|
|
assert is_ok(again)
|
|
assert again.value.client == ClientEndpoint(host="10.0.0.2", port=62032)
|
|
assert len(svc.list_slots()) == 1
|
|
|
|
|
|
def test_attach_rejects_blacklisted_peer() -> None:
|
|
svc = _service(black_list=(1234567,))
|
|
result = svc.attach_client(_PEER_A, "10.0.0.1", 62031)
|
|
assert is_fail(result)
|
|
|
|
|
|
def test_attach_fails_when_max_peers_exceeded() -> None:
|
|
svc = _service(max_peers=4)
|
|
for n in range(4):
|
|
peer = bytes_4(1000 + n)
|
|
assert is_ok(svc.attach_client(peer, "10.0.0.1", 62031 + n))
|
|
assert is_fail(svc.attach_client(bytes_4(9999), "10.0.0.9", 62099))
|
|
|
|
|
|
def test_detach_removes_session() -> None:
|
|
svc = _service(max_peers=4)
|
|
slot = svc.attach_client(_PEER_A, "10.0.0.1", 62031).value
|
|
removed = svc.detach_client(_PEER_A)
|
|
assert removed == slot
|
|
assert svc.resolve_client(_PEER_A) is None
|
|
assert is_ok(svc.attach_client(_PEER_B, "10.0.0.5", 62035))
|
|
|
|
|
|
def test_resolve_client_by_peer_id() -> None:
|
|
svc = _service()
|
|
svc.attach_client(_PEER_A, "192.168.1.10", 12345)
|
|
client = svc.resolve_client(_PEER_A)
|
|
assert client is not None
|
|
assert client.host == "192.168.1.10"
|
|
assert client.port == 12345
|
|
|
|
|
|
def test_expire_session_returns_teardown_plan() -> None:
|
|
svc = _service()
|
|
svc.attach_client(_PEER_A, "10.0.0.1", 62031)
|
|
teardown = svc.expire_session(_PEER_A)
|
|
assert teardown is not None
|
|
assert teardown.peer_id == _PEER_A
|
|
assert teardown.client.host == "10.0.0.1"
|
|
assert svc.resolve_client(_PEER_A) is None
|
|
|
|
|
|
def test_ip_blacklist_blocks_attach() -> None:
|
|
from adn_server.infrastructure.proxy import InMemoryProxyIpBlacklist
|
|
|
|
bl = InMemoryProxyIpBlacklist()
|
|
bl.block_until("10.0.0.9", time.time() + 60)
|
|
svc = ProxyUseCases(
|
|
InMemoryProxySlotStore(),
|
|
InMemoryPendingRptoQueue(),
|
|
ip_blacklist=bl,
|
|
)
|
|
assert is_fail(svc.attach_client(_PEER_A, "10.0.0.9", 62031))
|
|
|
|
|
|
def test_attach_client_assigns_report_slot() -> None:
|
|
svc = _service(max_peers=4)
|
|
first = svc.attach_client(_PEER_A, "10.0.0.1", 62031)
|
|
second = svc.attach_client(_PEER_B, "10.0.0.2", 62032)
|
|
assert is_ok(first)
|
|
assert is_ok(second)
|
|
assert first.value.report_slot == 0
|
|
assert second.value.report_slot == 1
|
|
|
|
|
|
def test_schedule_and_dequeue_rpto() -> None:
|
|
svc = _service()
|
|
svc.attach_client(_PEER_A, "10.0.0.1", 62031)
|
|
payload = b"TS1=123;TS2=456;"
|
|
assert svc.schedule_rpto(_PEER_A, payload) is True
|
|
assert svc.schedule_rpto(bytes_4(999), payload) is False
|
|
pending = svc.next_pending_rpto()
|
|
assert pending is not None
|
|
assert pending.peer_id == _PEER_A
|
|
assert pending.payload == payload
|
|
assert pending.client == ClientEndpoint(host="10.0.0.1", port=62031)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("data", "from_master", "expected"),
|
|
[
|
|
(b"RPTL" + _PEER_A + b"\x00", False, _PEER_A),
|
|
(b"DMRD" + b"\x00" * 7 + _PEER_A + b"\x00", False, _PEER_A),
|
|
(b"DMRD" + b"\x00" * 7 + _PEER_B, True, _PEER_B),
|
|
(b"RPTA" + b"\x00" * 2 + _PEER_A, True, _PEER_A),
|
|
(b"RPTL\x00", False, None),
|
|
],
|
|
)
|
|
def test_peer_id_from_packet(data: bytes, from_master: bool, expected: bytes | None) -> None:
|
|
assert peer_id_from_packet(data, from_master=from_master) == expected
|