homelab-codex-ws/scripts/maintenance/tests/test_cleanup_event_backlog.py

112 lines
4.4 KiB
Python
Raw Permalink Normal View History

"""Tests for cleanup_event_backlog.py — the event-flood backlog cleanup script.
Covers the safety invariant the whole cleanup depends on: only events that are
(a) a noise type, (b) strictly older than the observer's per-node checkpoint,
and (c) past the extra min-age safety buffer are ever deleted. Everything
else unprocessed events, real signals, foreign/ghost node dirs survives.
"""
from __future__ import annotations
import json
import sys
import time
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
import cleanup_event_backlog as cleanup # noqa: E402
def _write(node_dir: Path, ts: int, etype: str, svc: str = "svc") -> Path:
p = node_dir / f"evt-{node_dir.name}-{ts}-{etype}-{svc}.json"
p.write_text("{}")
return p
def test_deletes_only_processed_noise_past_buffer(tmp_path):
events_dir = tmp_path / "events"
node_dir = events_dir / "piha"
node_dir.mkdir(parents=True)
now = int(time.time())
checkpoint = now - 100
old_healthy = _write(node_dir, now - 7200, "service_healthy")
old_node_health = _write(node_dir, now - 7200, "node_health")
recent_healthy = _write(node_dir, now - 30, "service_healthy", svc="svc2") # within buffer
unprocessed = _write(node_dir, now + 50, "service_healthy", svc="svc3") # after checkpoint
real_signal = _write(node_dir, now - 999999, "healthcheck_failed")
checkpoints = {"piha": checkpoint}
candidates, counts = cleanup.scan(
{"service_healthy", "node_health"}, 3600, checkpoints, now, events_dir
)
assert set(candidates) == {old_healthy, old_node_health}
for f in (recent_healthy, unprocessed, real_signal):
assert f.exists()
assert counts["piha"]["healthcheck_failed"]["delete"] == 0
assert counts["piha"]["healthcheck_failed"]["keep"] == 1
def test_never_deletes_without_checkpoint_for_node(tmp_path):
"""A node dir with no checkpoint entry (ghost/foreign dir) must be left alone."""
events_dir = tmp_path / "events"
node_dir = events_dir / "be17cb6eb0f6"
node_dir.mkdir(parents=True)
now = int(time.time())
_write(node_dir, now - 999999, "service_healthy")
candidates, counts = cleanup.scan(
{"service_healthy", "node_health"}, 3600, {}, now, events_dir
)
assert candidates == []
assert counts["be17cb6eb0f6"]["service_healthy"]["keep"] == 1
def test_never_deletes_incidents_or_unknown_types(tmp_path):
events_dir = tmp_path / "events"
node_dir = events_dir / "vps"
node_dir.mkdir(parents=True)
now = int(time.time())
checkpoint = now - 100
ha_event = _write(node_dir, now - 999999, "ha_entity_unavailable_long")
incident_like = _write(node_dir, now - 999999, "containers_not_running")
candidates, _counts = cleanup.scan(
{"service_healthy", "node_health"}, 3600, {"vps": checkpoint}, now, events_dir
)
assert candidates == []
assert ha_event.exists()
assert incident_like.exists()
def test_apply_actually_deletes_and_dry_run_does_not(tmp_path, capsys, monkeypatch):
events_dir = tmp_path / "events"
state_dir = tmp_path / "state"
node_dir = events_dir / "piha"
node_dir.mkdir(parents=True)
state_dir.mkdir(parents=True)
now = int(time.time())
checkpoint = now - 100
old_healthy = _write(node_dir, now - 7200, "service_healthy")
(state_dir / "observer_checkpoint.json").write_text(
json.dumps({"node_checkpoints": {"piha": checkpoint}})
)
monkeypatch.setattr(cleanup, "EVENTS_DIR", events_dir)
monkeypatch.setattr(cleanup, "CHECKPOINT_FILE", state_dir / "observer_checkpoint.json")
monkeypatch.setattr(sys, "argv", ["cleanup_event_backlog.py"])
cleanup.main()
assert old_healthy.exists() # dry run: untouched
monkeypatch.setattr(sys, "argv", ["cleanup_event_backlog.py", "--apply"])
cleanup.main()
assert not old_healthy.exists() # applied: deleted
def test_type_extraction_matches_node_agent_filename_convention():
assert cleanup._type_from_name("evt-piha-1784215978-service_healthy-mosquitto.json") == "service_healthy"
assert cleanup._type_from_name("evt-vps-1784215978-node_health-node.json") == "node_health"
assert cleanup._type_from_name("evt-piha-1784215978-healthcheck_failed-z2m.json") == "healthcheck_failed"
assert cleanup._type_from_name("evt-piha-1784215978-ha_entity_unavailable_long-node.json") == \
"ha_entity_unavailable_long"