This repository has been archived on 2026-02-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
breakpilot-pwa/agent-core/tests/test_heartbeat.py
Benjamin Admin bfdaf63ba9 fix: Restore all files lost during destructive rebase
A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.

This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).

Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 09:51:32 +01:00

202 lines
5.9 KiB
Python

"""
Tests for Heartbeat Monitoring
Tests cover:
- Heartbeat registration and updates
- Timeout detection
- Pause/resume functionality
- Status reporting
"""
import pytest
import asyncio
from datetime import datetime, timezone, timedelta
from unittest.mock import AsyncMock
import sys
sys.path.insert(0, str(__file__).rsplit('/tests/', 1)[0])
from sessions.heartbeat import HeartbeatMonitor, HeartbeatClient, HeartbeatEntry
class TestHeartbeatMonitor:
"""Tests for HeartbeatMonitor"""
@pytest.fixture
def monitor(self):
"""Create a heartbeat monitor"""
return HeartbeatMonitor(
timeout_seconds=5,
check_interval_seconds=1,
max_missed_beats=2
)
def test_register_session(self, monitor):
"""Should register session for monitoring"""
monitor.register("session-1", "tutor-agent")
assert "session-1" in monitor.sessions
assert monitor.sessions["session-1"].agent_type == "tutor-agent"
def test_beat_updates_timestamp(self, monitor):
"""Beat should update last_beat timestamp"""
monitor.register("session-1", "agent")
original = monitor.sessions["session-1"].last_beat
import time
time.sleep(0.01)
result = monitor.beat("session-1")
assert result is True
assert monitor.sessions["session-1"].last_beat > original
assert monitor.sessions["session-1"].missed_beats == 0
def test_beat_nonexistent_session(self, monitor):
"""Beat should return False for unregistered session"""
result = monitor.beat("nonexistent")
assert result is False
def test_unregister_session(self, monitor):
"""Should unregister session from monitoring"""
monitor.register("session-1", "agent")
result = monitor.unregister("session-1")
assert result is True
assert "session-1" not in monitor.sessions
def test_pause_session(self, monitor):
"""Should pause monitoring for session"""
monitor.register("session-1", "agent")
result = monitor.pause("session-1")
assert result is True
assert "session-1" in monitor._paused_sessions
def test_resume_session(self, monitor):
"""Should resume monitoring for paused session"""
monitor.register("session-1", "agent")
monitor.pause("session-1")
result = monitor.resume("session-1")
assert result is True
assert "session-1" not in monitor._paused_sessions
def test_get_status(self, monitor):
"""Should return session status"""
monitor.register("session-1", "tutor-agent")
status = monitor.get_status("session-1")
assert status is not None
assert status["session_id"] == "session-1"
assert status["agent_type"] == "tutor-agent"
assert status["is_healthy"] is True
assert status["is_paused"] is False
def test_get_status_nonexistent(self, monitor):
"""Should return None for nonexistent session"""
status = monitor.get_status("nonexistent")
assert status is None
def test_get_all_status(self, monitor):
"""Should return status for all sessions"""
monitor.register("session-1", "agent-1")
monitor.register("session-2", "agent-2")
all_status = monitor.get_all_status()
assert len(all_status) == 2
assert "session-1" in all_status
assert "session-2" in all_status
def test_registered_count(self, monitor):
"""Should return correct registered count"""
assert monitor.registered_count == 0
monitor.register("s1", "a")
monitor.register("s2", "a")
assert monitor.registered_count == 2
def test_healthy_count(self, monitor):
"""Should return correct healthy count"""
monitor.register("s1", "a")
monitor.register("s2", "a")
# Both should be healthy initially
assert monitor.healthy_count == 2
# Simulate missed beat
monitor.sessions["s1"].missed_beats = 1
assert monitor.healthy_count == 1
class TestHeartbeatClient:
"""Tests for HeartbeatClient"""
@pytest.fixture
def monitor(self):
"""Create a monitor for the client"""
return HeartbeatMonitor(timeout_seconds=5)
def test_client_creation(self, monitor):
"""Client should be created with correct settings"""
client = HeartbeatClient(
session_id="session-1",
monitor=monitor,
interval_seconds=2
)
assert client.session_id == "session-1"
assert client.interval == 2
assert client._running is False
@pytest.mark.asyncio
async def test_client_start_stop(self, monitor):
"""Client should start and stop correctly"""
monitor.register("session-1", "agent")
client = HeartbeatClient(
session_id="session-1",
monitor=monitor,
interval_seconds=1
)
await client.start()
assert client._running is True
await asyncio.sleep(0.1)
await client.stop()
assert client._running is False
@pytest.mark.asyncio
async def test_client_context_manager(self, monitor):
"""Client should work as context manager"""
monitor.register("session-1", "agent")
async with HeartbeatClient("session-1", monitor, 1) as client:
assert client._running is True
assert client._running is False
class TestHeartbeatEntry:
"""Tests for HeartbeatEntry dataclass"""
def test_entry_creation(self):
"""Entry should be created with correct values"""
entry = HeartbeatEntry(
session_id="session-1",
agent_type="tutor-agent",
last_beat=datetime.now(timezone.utc)
)
assert entry.session_id == "session-1"
assert entry.agent_type == "tutor-agent"
assert entry.missed_beats == 0