| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369 |
- # -*- coding: utf-8 -*-
- """
- Tests für Network KeepAlive.
- """
- import asyncio
- import pytest
- from datetime import datetime, timedelta
- from trixy_core.network.keepalive import (
- KeepAliveManager,
- KeepAliveSession,
- KeepAliveConfig,
- ConnectionState,
- ConnectionStats,
- )
- class TestKeepAliveConfig:
- """Tests für KeepAliveConfig."""
- def test_default_config(self):
- """Testet Standard-Konfiguration."""
- config = KeepAliveConfig()
- assert config.ping_interval == 30.0
- assert config.pong_timeout == 10.0
- assert config.max_missed_pongs == 3
- def test_custom_config(self):
- """Testet benutzerdefinierte Konfiguration."""
- config = KeepAliveConfig(
- ping_interval=60.0,
- pong_timeout=5.0,
- max_missed_pongs=5,
- )
- assert config.ping_interval == 60.0
- assert config.pong_timeout == 5.0
- assert config.max_missed_pongs == 5
- class TestConnectionStats:
- """Tests für ConnectionStats."""
- def test_initial_stats(self):
- """Testet initiale Statistiken."""
- stats = ConnectionStats()
- assert stats.pings_sent == 0
- assert stats.pongs_received == 0
- assert stats.missed_pongs == 0
- assert stats.state == ConnectionState.CONNECTED
- def test_state_values(self):
- """Testet Connection-States."""
- assert ConnectionState.CONNECTED.name == "CONNECTED"
- assert ConnectionState.DISCONNECTED.name == "DISCONNECTED"
- assert ConnectionState.STALE.name == "STALE"
- class TestKeepAliveSession:
- """Tests für KeepAliveSession."""
- @pytest.fixture
- def config(self):
- """Erstellt Test-Konfiguration."""
- return KeepAliveConfig(
- ping_interval=0.1, # Schnelle Tests
- pong_timeout=0.05,
- max_missed_pongs=2,
- initial_delay=0.01,
- )
- @pytest.fixture
- def ping_func(self):
- """Erstellt Mock-Ping-Funktion."""
- calls = []
- async def ping():
- calls.append(datetime.now())
- ping.calls = calls
- return ping
- @pytest.mark.asyncio
- async def test_session_creation(self, config, ping_func):
- """Testet Session-Erstellung."""
- session = KeepAliveSession(
- connection_id="test-001",
- ping_func=ping_func,
- config=config,
- )
- assert session.connection_id == "test-001"
- assert session.state == ConnectionState.CONNECTED
- assert not session.is_running
- @pytest.mark.asyncio
- async def test_session_start_stop(self, config, ping_func):
- """Testet Start/Stop."""
- session = KeepAliveSession(
- connection_id="test-001",
- ping_func=ping_func,
- config=config,
- )
- await session.start()
- assert session.is_running
- await asyncio.sleep(0.05)
- await session.stop()
- assert not session.is_running
- @pytest.mark.asyncio
- async def test_handle_pong(self, config, ping_func):
- """Testet Pong-Verarbeitung."""
- session = KeepAliveSession(
- connection_id="test-001",
- ping_func=ping_func,
- config=config,
- )
- await session.start()
- await asyncio.sleep(0.02)
- # Simuliere Ping gesendet
- session._waiting_for_pong = True
- session._ping_sent_time = datetime.now()
- session.stats.pings_sent = 1
- # Pong empfangen
- session.handle_pong()
- assert session.stats.pongs_received == 1
- assert not session._waiting_for_pong
- await session.stop()
- @pytest.mark.asyncio
- async def test_rtt_calculation(self, config, ping_func):
- """Testet RTT-Berechnung."""
- session = KeepAliveSession(
- connection_id="test-001",
- ping_func=ping_func,
- config=config,
- )
- # Simuliere Ping/Pong
- session._waiting_for_pong = True
- session._ping_sent_time = datetime.now() - timedelta(milliseconds=50)
- session.handle_pong()
- assert session.stats.last_rtt_ms >= 50
- assert session.stats.min_rtt_ms >= 50
- assert session.stats.max_rtt_ms >= 50
- class TestKeepAliveManager:
- """Tests für KeepAliveManager."""
- @pytest.fixture
- def config(self):
- """Erstellt Test-Konfiguration."""
- return KeepAliveConfig(
- ping_interval=0.1,
- pong_timeout=0.05,
- max_missed_pongs=2,
- initial_delay=0.01,
- )
- @pytest.fixture
- def manager(self, config):
- """Erstellt KeepAliveManager."""
- return KeepAliveManager(default_config=config)
- @pytest.mark.asyncio
- async def test_register_connection(self, manager):
- """Testet Verbindungs-Registrierung."""
- async def ping():
- pass
- session = await manager.register("conn-001", ping, auto_start=False)
- assert session is not None
- assert manager.session_count == 1
- assert "conn-001" in manager._sessions
- @pytest.mark.asyncio
- async def test_unregister_connection(self, manager):
- """Testet Verbindungs-Entfernung."""
- async def ping():
- pass
- await manager.register("conn-001", ping, auto_start=False)
- assert await manager.unregister("conn-001")
- assert manager.session_count == 0
- @pytest.mark.asyncio
- async def test_unregister_nonexistent(self, manager):
- """Testet Entfernung nicht existierender Verbindung."""
- assert not await manager.unregister("nonexistent")
- @pytest.mark.asyncio
- async def test_get_session(self, manager):
- """Testet Session-Abruf."""
- async def ping():
- pass
- await manager.register("conn-001", ping, auto_start=False)
- session = manager.get_session("conn-001")
- assert session is not None
- assert session.connection_id == "conn-001"
- @pytest.mark.asyncio
- async def test_handle_pong(self, manager):
- """Testet Pong-Verarbeitung über Manager."""
- async def ping():
- pass
- await manager.register("conn-001", ping, auto_start=False)
- # Simuliere Ping
- session = manager.get_session("conn-001")
- session._waiting_for_pong = True
- session._ping_sent_time = datetime.now()
- session.stats.pings_sent = 1
- manager.handle_pong("conn-001")
- assert session.stats.pongs_received == 1
- @pytest.mark.asyncio
- async def test_get_stats(self, manager):
- """Testet Statistik-Abruf."""
- async def ping():
- pass
- await manager.register("conn-001", ping, auto_start=False)
- stats = manager.get_stats("conn-001")
- assert stats is not None
- assert isinstance(stats, ConnectionStats)
- @pytest.mark.asyncio
- async def test_get_all_stats(self, manager):
- """Testet Abruf aller Statistiken."""
- async def ping():
- pass
- await manager.register("conn-001", ping, auto_start=False)
- await manager.register("conn-002", ping, auto_start=False)
- all_stats = manager.get_all_stats()
- assert len(all_stats) == 2
- assert "conn-001" in all_stats
- assert "conn-002" in all_stats
- @pytest.mark.asyncio
- async def test_get_summary(self, manager):
- """Testet Zusammenfassung."""
- async def ping():
- pass
- await manager.register("conn-001", ping, auto_start=False)
- summary = manager.get_summary()
- assert "total_sessions" in summary
- assert "active_sessions" in summary
- assert "states" in summary
- @pytest.mark.asyncio
- async def test_start_stop_all(self, manager):
- """Testet Start/Stop aller Sessions."""
- async def ping():
- pass
- await manager.register("conn-001", ping, auto_start=False)
- await manager.register("conn-002", ping, auto_start=False)
- assert manager.active_count == 0
- await manager.start_all()
- assert manager.active_count == 2
- await manager.stop_all()
- assert manager.active_count == 0
- @pytest.mark.asyncio
- async def test_timeout_callback(self, manager):
- """Testet Timeout-Callback."""
- timeout_received = []
- async def on_timeout(conn_id):
- timeout_received.append(conn_id)
- manager.on_timeout(on_timeout)
- async def ping():
- pass
- await manager.register("conn-001", ping, auto_start=False)
- # Simuliere Timeout
- await manager._global_timeout_handler("conn-001")
- assert "conn-001" in timeout_received
- @pytest.mark.asyncio
- async def test_disconnect_callback(self, manager):
- """Testet Disconnect-Callback."""
- disconnect_received = []
- async def on_disconnect(conn_id):
- disconnect_received.append(conn_id)
- manager.on_disconnect(on_disconnect)
- async def ping():
- pass
- await manager.register("conn-001", ping, auto_start=False)
- # Simuliere Disconnect
- await manager._global_disconnect_handler("conn-001")
- assert "conn-001" in disconnect_received
- @pytest.mark.asyncio
- async def test_get_stale_connections(self, manager):
- """Testet Erkennung von Stale-Verbindungen."""
- async def ping():
- pass
- await manager.register("conn-001", ping, auto_start=False)
- session = manager.get_session("conn-001")
- session.stats.state = ConnectionState.STALE
- stale = manager.get_stale_connections()
- assert "conn-001" in stale
- @pytest.mark.asyncio
- async def test_get_disconnected(self, manager):
- """Testet Erkennung getrennter Verbindungen."""
- async def ping():
- pass
- await manager.register("conn-001", ping, auto_start=False)
- session = manager.get_session("conn-001")
- session.stats.state = ConnectionState.DISCONNECTED
- disconnected = manager.get_disconnected()
- assert "conn-001" in disconnected
- @pytest.mark.asyncio
- async def test_cleanup(self, manager):
- """Testet Cleanup."""
- async def ping():
- pass
- await manager.register("conn-001", ping, auto_start=False)
- await manager.register("conn-002", ping, auto_start=False)
- # Markiere eine als disconnected
- session = manager.get_session("conn-001")
- session.stats.state = ConnectionState.DISCONNECTED
- removed = await manager.cleanup()
- assert removed == 1
- assert manager.session_count == 1
|