From e9e16e6b7842e882d6dd51c43d0e5f0a179cace2 Mon Sep 17 00:00:00 2001 From: emi Date: Tue, 21 Jul 2026 16:32:26 +0000 Subject: [PATCH] Remove the old scripts/dashboard.py (moved to thermograph-observability) (#11) --- core/metrics.py | 6 ++-- tests/core/test_dashboard.py | 56 ------------------------------------ 2 files changed, 4 insertions(+), 58 deletions(-) delete mode 100644 tests/core/test_dashboard.py diff --git a/core/metrics.py b/core/metrics.py index a12cd77..ff6e4ee 100644 --- a/core/metrics.py +++ b/core/metrics.py @@ -5,8 +5,10 @@ Cheap, best-effort counters for **inbound** HTTP requests (bucketed by category) since-start; durable daily history lives in the audit/error JSONL logs (see ``audit.py``). Recording never raises into the request path. -Read over the gated ``GET {BASE}/api/v2/metrics`` route (see ``app.py``) and rendered -by ``scripts/dashboard.py`` over SSH. +Read over the gated ``GET {BASE}/api/v2/metrics`` route (see ``app.py``) — a raw +counters/heartbeats API. (Dashboards proper now live in the separate +``thermograph-observability`` project, a Grafana + Loki stack fed from the JSON +logs; this endpoint remains for quick token-gated checks over an SSH tunnel.) **Multi-worker.** Under a single uvicorn worker the counters live in process memory. When the app runs several workers (``--workers N``), per-process memory would split the diff --git a/tests/core/test_dashboard.py b/tests/core/test_dashboard.py deleted file mode 100644 index 20192f8..0000000 --- a/tests/core/test_dashboard.py +++ /dev/null @@ -1,56 +0,0 @@ -"""Tests for the ops dashboard's daemon health rendering (scripts/dashboard.py). - -The single-leader subscription notifier must be judged by heartbeat freshness, not by -whether its thread happens to live on the worker the dashboard polled — otherwise ~2/3 -of polls (the non-leader workers) would cry DOWN for a perfectly healthy notifier. -""" -import importlib.util -import os - -REPO = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) -_spec = importlib.util.spec_from_file_location( - "dashboard", os.path.join(REPO, "scripts", "dashboard.py")) -dashboard = importlib.util.module_from_spec(_spec) -_spec.loader.exec_module(dashboard) - -C = dashboard.C(on=False) # no ANSI colors, so we can assert on plain text - - -def _mark(name, tnames, heartbeats): - return dashboard._daemon_mark(name, tnames, heartbeats, C) - - -def test_notifier_fresh_heartbeat_is_up_even_when_thread_absent(): - # The polled worker is a non-leader (no notifier thread), but a fresh shared heartbeat - # proves the leader is running it. - mark, detail = _mark("subscription-notifier", ["MainThread", "neighbor-warmer"], - {"subscription-notifier": {"age_s": 120.0, "interval_s": 900}}) - assert mark == "up" - assert "2m ago" in detail - - -def test_notifier_stale_heartbeat_is_down(): - # Beat older than two intervals + slack => genuinely stuck/dead, flag it. - mark, detail = _mark("subscription-notifier", ["MainThread"], - {"subscription-notifier": {"age_s": 4_000.0, "interval_s": 900}}) - assert mark == "DOWN" - assert "stale" in detail - - -def test_notifier_missing_heartbeat_is_down(): - mark, detail = _mark("subscription-notifier", ["MainThread"], {}) - assert mark == "DOWN" - assert "no heartbeat" in detail - - -def test_neighbor_warmer_still_uses_per_worker_thread_check(): - # The warmer runs in every worker, so thread presence is the right signal and a - # heartbeat is neither written nor consulted. - assert _mark("neighbor-warmer", ["MainThread", "neighbor-warmer"], {})[0] == "up" - assert _mark("neighbor-warmer", ["MainThread"], {})[0] == "DOWN" - - -def test_short_dur_formats(): - assert dashboard._short_dur(45) == "45s" - assert dashboard._short_dur(120) == "2m" - assert dashboard._short_dur(3 * 3600 + 5 * 60) == "3h05m"