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
BreakPilot Dev 19855efacc
Some checks failed
Tests / Go Tests (push) Has been cancelled
Tests / Python Tests (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / Go Lint (push) Has been cancelled
Tests / Python Lint (push) Has been cancelled
Tests / Security Scan (push) Has been cancelled
Tests / All Checks Passed (push) Has been cancelled
Security Scanning / Secret Scanning (push) Has been cancelled
Security Scanning / Dependency Vulnerability Scan (push) Has been cancelled
Security Scanning / Go Security Scan (push) Has been cancelled
Security Scanning / Python Security Scan (push) Has been cancelled
Security Scanning / Node.js Security Scan (push) Has been cancelled
Security Scanning / Docker Image Security (push) Has been cancelled
Security Scanning / Security Summary (push) Has been cancelled
CI/CD Pipeline / Go Tests (push) Has been cancelled
CI/CD Pipeline / Python Tests (push) Has been cancelled
CI/CD Pipeline / Website Tests (push) Has been cancelled
CI/CD Pipeline / Linting (push) Has been cancelled
CI/CD Pipeline / Security Scan (push) Has been cancelled
CI/CD Pipeline / Docker Build & Push (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / CI Summary (push) Has been cancelled
ci/woodpecker/manual/build-ci-image Pipeline was successful
ci/woodpecker/manual/main Pipeline failed
feat: BreakPilot PWA - Full codebase (clean push without large binaries)
All services: admin-v2, studio-v2, website, ai-compliance-sdk,
consent-service, klausur-service, voice-service, and infrastructure.
Large PDFs and compiled binaries excluded via .gitignore.
2026-02-11 13:25:58 +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