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.
ADN-DMR-Peer-Server/tests/infrastructure/test_proxy_reload.py

136 lines
4.9 KiB

# ADN DMR Peer Server - tests infrastructure proxy reload
#
# Copyright (C) 2026 Rodrigo Pérez, CE5RPY <ce5rpy@qmd.cl>
#
###############################################################################
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
###############################################################################
"""Proxy hot-reload keeps LISTEN_PORT and active sessions."""
from __future__ import annotations
import logging
from unittest.mock import MagicMock
from adn_server.application.proxy import ProxyUseCases
from adn_server.infrastructure.config_reload import merge_top_level_config
from adn_server.domain.proxy import ClientEndpoint, ClientSlot
from adn_server.infrastructure.proxy.ip_blacklist import InMemoryProxyIpBlacklist
from adn_server.infrastructure.proxy.rpto_queue import InMemoryPendingRptoQueue
from adn_server.infrastructure.proxy.runtime import ProxyServiceState, apply_proxy_config_reload
from adn_server.infrastructure.proxy.slot_store import InMemoryProxySlotStore
from adn_server.infrastructure.proxy.udp_fanin import ProxyFanInProtocol
def _minimal_proxy_state() -> ProxyServiceState:
use_cases = ProxyUseCases(
InMemoryProxySlotStore(),
InMemoryPendingRptoQueue(),
max_peers=10,
black_list=(),
ip_blacklist=InMemoryProxyIpBlacklist(),
)
peer = b"\x00\x07\x06\xf5" # 730039101
use_cases._slots.bind( # noqa: SLF001
ClientSlot(
peer_id=peer,
client=ClientEndpoint(host="10.0.0.1", port=62031),
report_slot=0,
)
)
fanin = ProxyFanInProtocol(use_cases, master_sink=None) # type: ignore[arg-type]
return ProxyServiceState(
target_system="SYSTEM",
use_cases=use_cases,
master_sink=None, # type: ignore[arg-type]
client_sender=None, # type: ignore[arg-type]
fanin=fanin,
udp_port=object(),
listen_port=62031,
listen_ip="",
_runtime={
"listen_port": 62031,
"listen_ip": "",
"target_system": "SYSTEM",
"timeout": 30.0,
"debug": False,
"client_info": True,
"black_list": (),
"ip_black_list": {},
"max_peers": 10,
},
)
def test_apply_proxy_config_reload_keeps_sessions_and_updates_timeout() -> None:
state = _minimal_proxy_state()
config = {
"PROXY": {
"LISTEN_PORT": 62031,
"TARGET_SYSTEM": "SYSTEM",
"TIMEOUT": 45,
"DEBUG": True,
},
"SYSTEMS": {"SYSTEM": {"MAX_PEERS": 20}},
}
log = logging.getLogger("test.proxy.reload")
apply_proxy_config_reload(state, config, logger=log)
assert len(state.use_cases.list_slots()) == 1
assert state.udp_port is not None
assert state._runtime["timeout"] == 45.0
assert state.fanin.debug is True
assert state.use_cases._max_peers == 20 # noqa: SLF001
def test_hot_reload_never_closes_udp_listener() -> None:
"""Regression: SIGHUP must not stopListening() on PROXY (EADDRINUSE / dropped sessions)."""
state = _minimal_proxy_state()
stop_mock = MagicMock(return_value=None)
state.udp_port = MagicMock()
state.udp_port.stopListening = stop_mock
apply_proxy_config_reload(
state,
{
"PROXY": {"LISTEN_PORT": 62031, "TARGET_SYSTEM": "SYSTEM", "TIMEOUT": 60},
"SYSTEMS": {"SYSTEM": {"MAX_PEERS": 20}},
},
logger=logging.getLogger("test.proxy.reload"),
)
stop_mock.assert_not_called()
assert len(state.use_cases.list_slots()) == 1
def test_merge_top_level_config_updates_proxy_section() -> None:
live = {"GLOBAL": {}, "PROXY": {"TIMEOUT": 30, "LISTEN_PORT": 62031}}
merge_top_level_config(live, {"PROXY": {"TIMEOUT": 45, "LISTEN_PORT": 62031}})
assert live["PROXY"]["TIMEOUT"] == 45
def test_proxy_use_cases_apply_runtime_settings() -> None:
uc = ProxyUseCases(
InMemoryProxySlotStore(),
InMemoryPendingRptoQueue(),
max_peers=5,
black_list=(1001,),
)
uc.apply_runtime_settings(max_peers=12, black_list=(2002, 3003))
assert uc._max_peers == 12 # noqa: SLF001
assert uc._black_list == frozenset({2002, 3003}) # noqa: SLF001

Powered by TurnKey Linux.