vigilar/tests/unit/test_health.py
Aaron D. Lee 2b3a4ba853 Add pet and wildlife counts to daily digest
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 13:24:33 -04:00

100 lines
4.2 KiB
Python

"""Tests for health monitoring."""
import shutil
import tempfile
from pathlib import Path
from unittest.mock import patch
from vigilar.health.digest import build_digest, format_digest
from vigilar.health.pruner import find_prunable_recordings, calculate_disk_usage_pct
from vigilar.health.monitor import HealthCheck, HealthStatus, check_disk, check_mqtt_port
from vigilar.storage.schema import pet_sightings, wildlife_sightings
class TestDiskCheck:
@patch("vigilar.health.monitor.shutil.disk_usage")
def test_healthy(self, mock_usage):
mock_usage.return_value = type("U", (), {"total": 100_000_000_000, "used": 50_000_000_000, "free": 50_000_000_000})()
result = check_disk("/var/vigilar", warn_pct=85, critical_pct=95)
assert result.status == HealthStatus.HEALTHY
@patch("vigilar.health.monitor.shutil.disk_usage")
def test_warning(self, mock_usage):
mock_usage.return_value = type("U", (), {"total": 100_000_000_000, "used": 90_000_000_000, "free": 10_000_000_000})()
result = check_disk("/var/vigilar", warn_pct=85, critical_pct=95)
assert result.status == HealthStatus.WARNING
@patch("vigilar.health.monitor.shutil.disk_usage")
def test_critical(self, mock_usage):
mock_usage.return_value = type("U", (), {"total": 100_000_000_000, "used": 97_000_000_000, "free": 3_000_000_000})()
result = check_disk("/var/vigilar", warn_pct=85, critical_pct=95)
assert result.status == HealthStatus.CRITICAL
class TestPruner:
def test_calculate_disk_pct(self):
with tempfile.TemporaryDirectory() as d:
pct = calculate_disk_usage_pct(d)
assert 0 <= pct <= 100
def test_find_prunable_empty(self, test_db):
result = find_prunable_recordings(test_db, limit=10)
assert result == []
class TestPetDigest:
def test_digest_includes_pet_sightings(self, test_db, tmp_data_dir):
import time
with test_db.begin() as conn:
conn.execute(pet_sightings.insert().values(
ts=time.time(), pet_id="p1", species="cat",
camera_id="kitchen", confidence=0.9, labeled=1,
))
conn.execute(pet_sightings.insert().values(
ts=time.time(), pet_id="p2", species="dog",
camera_id="kitchen", confidence=0.85, labeled=1,
))
data = build_digest(test_db, str(tmp_data_dir), since_hours=1)
assert data["pet_sightings"] == 2
def test_digest_includes_wildlife(self, test_db, tmp_data_dir):
import time
with test_db.begin() as conn:
conn.execute(wildlife_sightings.insert().values(
ts=time.time(), species="bear", threat_level="PREDATOR",
camera_id="front", confidence=0.9,
))
conn.execute(wildlife_sightings.insert().values(
ts=time.time(), species="deer", threat_level="PASSIVE",
camera_id="back", confidence=0.8,
))
data = build_digest(test_db, str(tmp_data_dir), since_hours=1)
assert data["wildlife_predators"] == 1
assert data["wildlife_passive"] == 1
def test_format_includes_pets(self):
data = {
"person_detections": 2, "unknown_vehicles": 0,
"recordings": 5, "disk_used_gb": 100.0, "disk_used_pct": 50,
"since_hours": 12, "pet_sightings": 15,
"wildlife_predators": 0, "wildlife_nuisance": 1, "wildlife_passive": 3,
}
text = format_digest(data)
assert "15 pet" in text
assert "wildlife" in text.lower() or "nuisance" in text.lower()
class TestMQTTCheck:
@patch("vigilar.health.monitor.socket.create_connection")
def test_mqtt_reachable(self, mock_conn):
mock_conn.return_value.__enter__ = lambda s: s
mock_conn.return_value.__exit__ = lambda s, *a: None
result = check_mqtt_port("127.0.0.1", 1883)
assert result.status == HealthStatus.HEALTHY
@patch("vigilar.health.monitor.socket.create_connection")
def test_mqtt_unreachable(self, mock_conn):
mock_conn.side_effect = ConnectionRefusedError()
result = check_mqtt_port("127.0.0.1", 1883)
assert result.status == HealthStatus.CRITICAL